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

How to enforce that a type implements a trait at compile time?

我想写这样一个宏:

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

但是 $t 应该实现 AddSubMul 特征。我如何在编译时检查它?

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

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

const _: () = {
    fn assert_send<T: Send>() {}
    fn assert_sync<T: Sync>() {}

    // RFC 2056
    fn assert_all() {
        assert_send::<MyType>();
        assert_sync::<MyType>();
    }
};

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

macro_rules! example {
    ($name:ident, $field:ty) => {
        struct $name {
            x: $field,
        }

        const _: () = {
            fn assert_add<T: std::ops::Add<$field, Output = $field>>() {}
            fn assert_mul<T: std::ops::Mul<$field, Output = $field>>() {}

            // RFC 2056
            fn assert_all() {
                assert_add::<$field>();
                assert_mul::<$field>();
            }
        };
    };
}

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

在这两种情况下,我们都会创建一个虚拟 const 值来确定函数及其调用的范围,避免名称冲突。

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

非常感谢 提供了支持非对象安全特征的更好版本。

值得强调的是 RFC 2056 这将允许在 where 子句中进行“微不足道”的约束。一旦实施,将接受这样的条款:

impl Foo for Bar
where 
    i32: Iterator,
{}

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