Use a function for standard logic, a trait to define shared behavior across types, and a macro only when you need to generate code or accept variable arguments that functions cannot handle.
// Function: Standard logic with fixed arguments
fn add(a: i32, b: i32) -> i32 { a + b }
// Trait: Shared behavior for different types
trait Printable {
fn print(&self);
}
// Macro: Code generation or variable arguments
macro_rules! vec_of {
($elem:expr; $n:expr) => {
vec![$elem; $n]
};
}