简体   繁体   中英

Unexpected Token in Rust Macro

I am trying to write a macro that expands to a collection of impls for some custom structs I'm writing. This is what I have:

pub trait ComponentWise : Clone + Copy {
  fn new(x: f32, y: f32, z: f32) -> Self;

  fn x(&self) -> f32;
  fn y(&self) -> f32;
  fn z(&self) -> f32;

  fn has_nans(&self) -> bool {
    f32::is_nan(self.x()) || 
    f32::is_nan(self.y()) || 
    f32::is_nan(self.z())
  }
}

macro_rules! component_wise_impls { 
  ($T:ty) => {
    impl ComponentWise for $T {
      fn new(x: f32, y: f32, z: f32) -> $T {
        let x: $T = $T { vals: [x, y, z] };
        debug_assert!(!x.has_nans());
        x
      }
   };
}

pub struct Vector {
  pub vals: [f32; 3]
}

component_wise_impls!(Vector);

Sadly, I'm being told:

error: unexpected token: Vector

let x: $T = $T { vals: [x, y, z] };

I'm a little surprised, since I can do Vector { vals: [x, y, z] } , and I would think that is exactly what $T { vals: [x, y, z] }; expands to. Is there a way of writing this new function in a macro?

This is working:

macro_rules! component_wise_impls {
    ($T:ident) => {
        impl ComponentWise for $T {
            fn new(x: f32, y: f32, z: f32) -> $T {
                let x: $T = $T { vals: [x, y, z] };
                debug_assert!(!x.has_nans());
                x
            }
        }
    }
}

You cannot use a ty where an expression is needed.

From this page :

The interpolation $argument_name may appear in any location consistent with its fragment specifier (ie, if it is specified as ident , it may be used anywhere an identifier is permitted).

As llogiq pointed out, you have to implement the x , y and z methods, so the complete code is:

pub trait ComponentWise : Clone + Copy {
  fn new(x: f32, y: f32, z: f32) -> Self;

  fn x(&self) -> f32;
  fn y(&self) -> f32;
  fn z(&self) -> f32;

  fn has_nans(&self) -> bool {
    f32::is_nan(self.x()) || 
    f32::is_nan(self.y()) || 
    f32::is_nan(self.z())
  }
}

macro_rules! component_wise_impls {
    ($T:ident) => {
        impl ComponentWise for $T {
            fn new(x: f32, y: f32, z: f32) -> $T {
                let x: $T = $T { vals: [x, y, z] };
                debug_assert!(!x.has_nans());
                x
            }

            fn x(&self) -> f32 {
                self.vals[0]
            }

            fn y(&self) -> f32 {
                self.vals[1]
            }

            fn z(&self) -> f32 {
                self.vals[2]
            }
        }
    }
}

#[derive(Copy, Clone)]
pub struct Vector {
  pub vals: [f32; 3]
}

component_wise_impls!(Vector);

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM