【问题标题】:Is it possible to automatically define fields of a struct?是否可以自动定义结构的字段?
【发布时间】:2017-07-21 08:31:48
【问题描述】:

我正在使用宏来实现特征作为我库的一部分。此实现要求结构至少有一个附加字段。

pub trait Trait {
    fn access_var(&mut self, var: bool);
}

macro_rules! impl_trait {
    (for $struct:ident) => {
        impl Trait for $struct {
            pub fn access_var(&mut self, var: bool) {
                self.var = var; // requires self to have a field 'var'
            }
        }
    }
}

我想防止用户每次都添加这些额外的字段。由于 Rust 编译器不允许在字段定义中使用宏(我没有这方面的来源,所以如果我错了请纠正我),这样的东西不起作用。

macro_rules! variables_for_trait {
    () => {
        var: bool,
    }   
};

struct Foo {
    variables_for_trait!(); // error: expected ':' found '!'
    additional_var: i64,
}

我想我可以创建一个宏来启用这样的功能

bar!(Foo with additional_var: i64, other_var: u64);

解析宏后的样子:

pub struct Foo {
   var: bool,
   additional_var: i64,
   other_var: u64,
}

impl Trait for Foo {
   pub fn access_var(&mut self, var: bool) {
        self.var = var;
   }
}

有没有更好的方法来解决这个问题,如果没有,你能给我一个bar!的示例语法吗?

P.S:像bar! 这样的名字有什么好听的?

【问题讨论】:

  • 要自动实现某些特征,您应该使用过程宏 doc.rust-lang.org/beta/book/first-edition/… 。然后你可以使用#[derive(MyTrait)]
  • hmm,虽然肯定是将来要记住的事情,但 #[derive(Trait)] 似乎不允许强制结构字段。所以它并不能完全解决我的问题......

标签: struct macros rust field traits


【解决方案1】:

我最终用 2 个不同的宏解决了这个问题:

// I simply copied it from my project and changed some names,
// But due to the fact that I couldn't think of better names for my macros,
// I just ended up using the original names, even though they don't quite fit

macro_rules! new_object_type {
    ($struct:ident {$( $field:ident:$type:ty ),*}) =>{
        pub struct $struct {
            var: bool,
            $(
                $field: $type,
            )*
        }

        impl Trait for $struct {
            pub fn access_var(&mut self, var: bool) {
                self.var = var;
            }
        }
    };
}

macro_rules! construct_object {
    ($struct:ident {$( $field:ident:$value:expr ),*}) => {
        $struct {
           var: false,
           $(
               $field: $value, 
           )*
       }
    };
}

要创建一个实现Trait 的新结构,您现在编写:

new_object_type!(Foo {
    additional_var: i64,
    other_var: u64,
});

要创建Foo 的新实例,请编写:

construct_object!(Foo {
    additional_var: 42,
    other_var: 82000,
})

现在可以使用Trait 而无需与var 交互。

这种方法的问题:

  • 它不像我希望的那样干净,而且没有适当的文档,它很难使用,特别是如果用户是 Rust 的新手。

  • 有两个相同的字段可能会出现一些问题,因为用户看不到所有已实现的变量(这可以通过将 var 的名称更改为 T7dkD3S3O8 之类的名称来解决,这肯定会让这不太可能发生)

  • 由于结构的定义和构造都在宏内部,因此错误消息可能更难理解

【讨论】:

    猜你喜欢
    • 2011-09-05
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-08-21
    • 1970-01-01
    相关资源
    最近更新 更多