Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Converting Rust macro types into expressions

Tags:

macros

rust

I'd like to assume a given type implements some trait (e.g. Default) with a method (e.g. default()). I want to call that method and store its value into a local variable. Here is a general idea of it:

macro_rules! get_default {
    (  $x:ty = $alias:ident ) => {
        let $alias = $x::default();
    };
}

fn main() {
    // get_default!(i32 = z); 
    // println!("get_default! {:?} ", z);
    println!("i32 default {:?} ", i32::default());
}

Playground link.

When I try that I get an error:

error: expected expression, found `i32`
 --> <anon>:3:22
  |>
3 |>         let $alias = $x::default();
  |>                      ^^

I understand it's because it expects an expression, but I want to limit input to types only. Is there a way to turn $x from ty to expr, or a way to call a method on a type (even if it's potentially missing).

like image 987
Daniel Fath Avatar asked Sep 12 '16 15:09

Daniel Fath


People also ask

Is macro_rules a macro?

macro_rules allows users to define syntax extension in a declarative way. We call such extensions "macros by example" or simply "macros". Each macro by example has a name, and one or more rules.

What does tt mean in Rust?

I'm reading a book about Rust, and start playing with Rust macros. All metavariable types are explained there and have examples, except the last one – tt . According to the book, it is a “a single token tree”.

What is the point of macros Rust?

Rust has excellent support for macros. Macros enable you to write code that writes other code, which is known as metaprogramming. Macros provide functionality similar to functions but without the runtime cost. There is some compile-time cost, however, since macros are expanded during compile time.

Are macros allowed in Rust?

The most widely used form of macros in Rust is the declarative macro. These are also sometimes referred to as “macros by example,” “ macro_rules! macros,” or just plain “macros.” At their core, declarative macros allow you to write something similar to a Rust match expression.


1 Answers

You were almost there. You can hint the expected default type to the compiler and then just use the universal function call syntax:

macro_rules! get_default {
    (  $x:ty = $alias:ident ) => {
        let $alias = <$x as Default>::default();
    };
}

fn main() {
    get_default!(i32 = z); 
    println!("get_default! {:?} ", z);
    println!("i32 default {:?} ", i32::default());
}

(Playground link)

The key bit is this:

let $alias = <$x as Default>::default();

This casts $x to the Default trait and then invokes the default() method, as you needed.

You can also use a shorthand when you don't need to disambiguate between traits:

let $alias = <$x>::default();

(Playground link)

More General Usage of UFCS

Using UFCS as shown above, you can disambiguate between traits that implement the same methods. This is the 'angle-bracket form' which is useful if the default() method is implemented in two traits.

In this specific scenario, you can also use UFCS more specifically, like so:

let $alias: $x = Default::default();

That alone provides enough information for Rust to infer the correct impl.

(Playground link)

like image 86
Aurora0001 Avatar answered Oct 05 '22 07:10

Aurora0001