News for hackers. Stuff that matters.
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.