[newbie ?] Why macros vs. functions?


#1

I am pretty new to system programming and rust. I was going through the Rust By Example and almost immediately (ok, immediately… heh) came across macros.

I get macros, I think, but I was left with “why?”. Would a function not accomplish the same thing? Is a macro, because it expands to source instead of adding to the call graph, be faster?

I guess I am looking for a “why macros?” explanation.

cheers


Since we have macros, why use functions at all?
#2
  1. It expands to something that is typesafe at compile time. In C, printf tries to format things runtime, based on the format string. That isn’t safe.
  2. It’s very fast: it’s possible to dispatch the formatters statically.
  3. It allows for variadic arguments, which functions don’t currently have support for.

Edit: Oh, btw, I’m explaining specifically about println! macro here, as you can probably guess!


#3

In addition to the above, macros allow you to encapsulate patterns that simply can’t be done using a function abstraction. For example, the try! macro conditionally returns from the enclosing function. There’s no way you could put this in a function, since the return would have to be non-local. Other macros allow you to declare types, functions, etc… that match a template. In both cases, the pattern being expressed is incompatible with the abstraction that functions provide.


#4

Thanks to the prompt replies. I think I understand a bit more. I very much appreciate the time to help a noob.


#5

@mackenza Here’s a concrete example. The assert! macro looks like this:

#[macro_export]
#[stable(feature = "rust1", since = "1.0.0")]
macro_rules! assert {
    ($cond:expr) => (
        if !$cond {
            panic!(concat!("assertion failed: ", stringify!($cond)))
        }
    );
    ($cond:expr, $($arg:tt)+) => (
        if !$cond {
            panic!($($arg)+)
        }
    );
}

Why is assert a macro? There are multiple reasons but the clearest is this: stringify!($cond). This is doing magic that is not possible within the language itself. stringify! takes (more-or-less) the raw syntactic tokens and, at compile-time, produces their representation as a string literal. IOW stringify!(1 == 2) expands to the static string slice “1 == 2”. This is what lets the assertion failure error message say what assertion failed.

Try to imagine how you would do that with a function:

fn assert(cond: bool) {
  // How do we recover what the expression was??
}