如何强制类型在编译时实现特征?

Lod*_*din 4 macros rust

我想写一个像这样的宏:

macro_rules! a {
    ( $n:ident, $t:ty ) => {
         struct $n {
             x: $t
         }
    }
}
Run Code Online (Sandbox Code Playgroud)

但是$t应该实现Add,SubMul特征.如何在编译时检查它?

She*_*ter 6

首先,解决没有宏的问题.一种解决方案是创建未记录的私有函数,如果不满足条件,将无法编译:

struct MyType {
    age: i32,
    name: String,
}

fn __assert_send()
where
    MyType: Send,
{}

fn __assert_sync()
where
    MyType: Sync,
{}

// RFC 2056
fn __assert_traits() {
    __assert_send();
    __assert_sync();
}
Run Code Online (Sandbox Code Playgroud)

然后,修改简单的解决方案以使用宏:

macro_rules! a {
    ($n:ident, $t:ty) => {
        struct $n {
            x: $t
        }

        impl $n {
            fn __assert_add()
            where
                $t: std::ops::Add<$t, Output = $t>
            {}

            fn __assert_mul()
            where
                $t: std::ops::Mul<$t, Output = $t>
            {}

            // RFC 2056
            fn __assert_traits() {
                Self::__assert_add();
                Self::__assert_mul();
            }
        }
    }
}

a!(Moo, u8);
a!(Woof, bool);

fn main() {}
Run Code Online (Sandbox Code Playgroud)

然后我会相信优化器在编译时删除代码,所以我不希望任何额外的膨胀.

主要得益于克里斯·摩根提供一个更好的版本的这种支持非对象的安全特性.

值得强调的是RFC 2056,它允许where子句中的"琐碎"约束.一旦实施,将接受这样的条款:

impl Foo for Bar
where 
    i32: Iterator,
{}
Run Code Online (Sandbox Code Playgroud)

在Rust的历史记录中,这种确切的行为已经多次改变,而RFC 2056则将其固定下来.为了在这种情况下保持我们想要的行为,我们需要从没有约束的另一个函数调用断言函数(因此必须始终为true).

  • 我会去找`fn _assert_add(),其中Self:Add <Self,Output = Self> {} fn _verify_assertions(){Self :: _ assert_add()}`,我自己.在我看来,稍微更清晰的错误消息,并且(更重要的是)更通用,因为它不要求特性是对象安全的. (3认同)
  • 哦是的,我没有完全阅读.但这没问题 - "$ t:添加<$ t,输出= $ t>"也可以.`where`子句可以包含*any*类型的东西,而不仅仅是与'Self`或泛型相关的东西. (2认同)