News for hackers. Stuff that matters.
Re:Rust 1.43 by harikb on Thursday April 23, 2020 @11:46AM (#22957231)
For a non-rust person's point of view, I assume a macro is something like a super-pre-processor that helps keep the code concise. In the example below, what portion is written ahead of time (by the person making the macro) and what portion is the macro usage?

  macro_rules! mac_trait {
      ($i:item) => {
          trait T { $i }
      }
  }
  
  mac_trait! {
      fn foo() {}
  }
Is this making it easier to implement a trait? interface?
Re:Rust 1.43 by ashtonkem on Thursday April 23, 2020 @11:59AM (#22957371)
In general as a rust programmer you'll end up consuming orders of magnitudes more macros than you'll ever write; from what I've seen macros are more commonly written in libraries to make their use smoother, and my experience in other Macro heavy languages was similar (Clojure, SBCL).

But as others said, the point is boilerplate reduction. You can write a macro that'll automate that process away. A lot of examples are about automatically implementing certain behaviors for structs, such as equality, but you'll also see extensive macro use in ORM libraries that can generate a lot of runtime code off of a smaller table definition macro.

Anything with an explanation point is a macro invocation, by the way.

Re:Rust 1.43 by justwalt on Thursday April 23, 2020 @12:33PM (#22957786)
“Anything with an explanation point is a macro invocation, by the way.”

Can you explain what this means, exactly?

Re:Rust 1.43 by steveklabnik on Thursday April 23, 2020 @12:37PM (#22957838)
This is a function invocation:

    foo(bar)
This is a macro invocation:

    foo!(bar)
It's done this way because macros can have much more complex things inside the ()s than functions can. This helps both humans and computers parse such things.
Re:Rust 1.43 by jack1243star on Thursday April 23, 2020 @12:38PM (#22957849)
Not OP, I think they mean the exclamation mark (!)
Re:Rust 1.43 by ashtonkem on Thursday April 23, 2020 @05:27PM (#22961674)
TIL that “exclamation point” is region specific. Exclamation mark sounds subtly wrong to my ear.
Re:Rust 1.43 by wtetzner on Monday April 27, 2020 @12:13PM (#22997194)
I thought the confusion was that you wrote "explanation point" instead of "exclamation point".
Re:Rust 1.43 by lalaithion on Thursday April 23, 2020 @12:38PM (#22957841)
foo(x, y, z) is a function. foo!(x, y, z) is a macro.
Re:Rust 1.43 by ccktlmazeltov on Thursday April 23, 2020 @12:41PM (#22957877)

    println!("something")

that's a macro
Re:Rust 1.43 by gnosek on Thursday April 23, 2020 @12:39PM (#22957859)
Exclamation mark. e.g.

    println!("hello, world");
or

    todo!();
are macro invocations.
Re:Rust 1.43 by staticassertion on Thursday April 23, 2020 @01:37PM (#22958542)
Macros:

#[Foo] struct Bar {}

baz!()

cat!{}

blah![]

Re:Rust 1.43 by hinkley on Thursday April 23, 2020 @02:06PM (#22958945)
exclamation point?
Re:Rust 1.43 by ashtonkem on Thursday April 23, 2020 @05:27PM (#22961670)
TIL that “Exclamation point” is region specific. Other people call it exclamation marks.
Re:Rust 1.43 by hinkley on Thursday April 23, 2020 @06:44PM (#22962419)
Top level says “explanation point” and apparently didn’t ever reread after getting odd sounding questions.
Re:Rust 1.43 by ashtonkem on Thursday April 23, 2020 @07:30PM (#22962855)
Uhh, I think you’re referring to me.

I didn’t bother to clarify, since 5 people jumped in to explain, and I felt it was redundant.

Re:Rust 1.43 by zamalek on Thursday April 23, 2020 @11:54AM (#22957308)
The first block (macro_rules!) the declaration is written by the macro author, the second block (mac_trait!) is the usage written by the user. Note that macro_rules is itself a macro.

`trait T {}` and `fn foo() {}` are used in the final code, which would be:

    trait T { fn foo(){} }
Re:Rust 1.43 by harikb on Thursday April 23, 2020 @12:00PM (#22957380)
Thanks. I am trying to understand where the savings for the user is (as in less code to type). I guess it is somehow related to T being a generic and if they didn't use this macro, they would have had to repeat this all over?
Re:Rust 1.43 by estebank on Thursday April 23, 2020 @12:09PM (#22957476)
In the example given the macro is almost useless. The benefit comes if you want the expanded code to have more things, like methods defined that otherwise you would have your consumer write or sister methods and types.

The macros are only about convenience on usage and reduction of boilerplate. A good example is the vec![1,2] macro call that expands to

    {
        let mut x = Vec::new();
        x.push(1);
        x.push(2);
        x
    }
The reason the blog post is using that example is because it's showing a new type of macro argument which accepts items (functions, structs, enums, traits and impls) and expands them correctly without any extra needs by the macro writer.
Re:Rust 1.43 by cesarb on Thursday April 23, 2020 @01:22PM (#22958378)
Just nitpicking, but a better explanation of the expansion of `vec![1,2]` would use `Vec::with_capacity(2)` instead of `Vec::new()`, that is, it already allocates the Vec with the correct capacity (the real expansion uses in-place construction of a boxed slice, but the effect is similar).
Re:Rust 1.43 by majewsky on Thursday April 23, 2020 @04:05PM (#22960564)
Here's a real-world example from the code I was working on yesterday: I have several database tables where the records have numeric IDs, and instead of having the ID type be just an int everywhere, I want them to be proper types to avoid mixups. Instead of

  fn set_document_owner(document_id: i64, user_id: i64)
where I could easily provide the arguments in the wrong order, I want

  fn set_document_owner(document_id: DocumentID, user_id: UserID)
where this cannot happen. Each ID type is just a one-element tuple containing an i64, which would just be

  pub struct DocumentID(pub i64);
  pub struct UserID(pub i64);
  //... and so on ...
and so on. But in order for them to behave as expected (e.g. with the == operator or when reading/writing IDs in the database), I need to write a bunch of boilerplate. This is the macro that I have now:

  use rusqlite::types::{FromSql, FromSqlResult, ToSql, ToSqlOutput, ValueRef};

  macro_rules! make_id_type {
      ( $name:ident ) => {
          ///A type-safe primary key.
          #[derive(Default, Clone, Copy, Hash, PartialEq, Eq, Deserialize, Serialize)]
          pub struct $name(pub i64);

          impl fmt::Display for $name {
              fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
                  self.0.fmt(f)
              } 
          } 

          impl ToSql for $name {
              fn to_sql(&self) -> rusqlite::Result<ToSqlOutput> {
                  self.0.to_sql()
              } 
          } 

          impl FromSql for $name {
              fn column_result(value: ValueRef) -> FromSqlResult<Self> {
                  Ok($name(i64::column_result(value)?))
              } 
          } 
      };
  }

  make_id_type!(DocumentID);
  make_id_type!(UserID);
  //... and so on ...
This is pretty much the most basic usecase for macros. You can do some more interesting stuff than just take a name and insert it into some template, because macros can actually pattern-match the phrases given to them in the macro invocation. A good example, also from the RDBMS domain, would be https://docs.diesel.rs/diesel/macro.table.html (link goes to the documentation of the macro).
Re:Rust 1.43 by CDSlice on Thursday April 23, 2020 @12:12PM (#22957512)
In this specific case the macro isn’t very useful since you can easily write `trait T { ... }` yourself. They tend to be more useful when there is a lot of similar boilerplate code that only differ in a few ways. A macro can cut down most of that repetition. They can also be used to provide pseudo “literals” like the vec! macro does for the Vec data type.
Re:Rust 1.43 by zamalek on Thursday April 23, 2020 @12:32PM (#22957779)
One thing I use it for all the time is data-driven tests, e.g. https://github.com/jcdickinson/racemus/blob/master/racemus-b...
Re:Rust 1.43 by aliceryhl on Thursday April 23, 2020 @12:06PM (#22957435)
One common usage is to use macros to repeat some definition for every integer type. In this case T is not a generic, but the name of the trait.
Re:Rust 1.43 by pornel on Thursday April 23, 2020 @03:12PM (#22959871)
It's a bit like C's:

    #define MAC_TRAIT(i) trait T { i }

    MAC_TRAIT( fn foo() {} )

Except that in Rust macro arguments are typed and can dictate their own microsyntax. Macro output is assembled in the syntax tree, not as text find'n'replace, which also avoids many surprises.
Re:Rust 1.43 by Fiahil on Thursday April 23, 2020 @11:55AM (#22957326)
Rust macros are their own little thing, they help reduce code repeatability by writing code in your place.

You don't need them to write rust applications, but they could, for example, help writing tests on 3 different backends without having to write 3 times the same testing function.

In this situation, it's different from having a type parameter, because macros won't make you change the function definition. They will just "copy and paste" the code block for you.

Re:Rust 1.43 by fulafel on Friday April 24, 2020 @01:27AM (#22964934)
Rust macros are real, structural macros, like in most macro supporting langugaes. So they are not like the preprocessor style macros in C and relatives.

A good mental model is to think about this kind of macro is as a compiler callback that defines a syntax tree fragment in its body, to be evaluated when the macro is called.

Re:Rust 1.43 by progval on Thursday April 23, 2020 @11:50AM (#22957258)
The first block defines a macro; the second block is the macro usage.

The goal of macros is to deduplicate code; either inside a function or to implement traits (eg. the Debug trait, that allows printing a structure/enumeration/... recursively; this would be very boring to implement yourself every time)

Re:Rust 1.43 by Koshkin on Thursday April 23, 2020 @12:00PM (#22957376)
The looks of this makes me want to go back to Lisp.
Re:Rust 1.43 by estebank on Thursday April 23, 2020 @12:12PM (#22957513)
This macro system is very powerful, if kludgy, but it's not the final version of the system. Macros 2.0 has been in the works for a while now and will eventually have a much nicer syntax for macro definitions. When Rust 1.0 was gearing up for release the team (rightly imo) decided that launching then with known-imperfect-but-usable macros was a worthwhile trade-off.
Re:Rust 1.43 by simias on Thursday April 23, 2020 @12:29PM (#22957743)
Rust's macros are unpleasant to write and debug but very nice to use. The language is also much more expressive than C so I don't find myself needing to (ab)use macros quite as often.

Of course lisp macros are on a different level but the languages are massively different so it's not necessarily a super fair comparison.

Re:Rust 1.43 by Eikon on Thursday April 23, 2020 @12:44PM (#22957918)
One issue with macros is that they are currently breaking most IDE integrations.
Re:Rust 1.43 by steveklabnik on Thursday April 23, 2020 @12:27PM (#22957709)
It was designed and implemented by some huge Racket fans, incidentally...