我不理解 trait,官方示例代码看上去和 Rust 的 trait 有很大区别,有人可以讲讲吗?

首先,trait 是动态分发吗?类型是在编译时单态化,还是运行时确定?

其次,为什么 trait 的实现,没有作用域,而是直接写函数?(我感觉很难分清楚给哪个 trait 实现了哪个 type)

再次,trait 可以给任意类型扩展函数吗?比如下述 Rust 代码:

trait A {...}
impl<T> A for T {...}

有等价的 moonbit 写法吗?

最后,我有一段 Rust 代码,是完全靠 trait 实现抽象的,可以翻译成 moonbit 吗:

struct Context;

trait Display<T> {
    fn display(x: T) -> String;
}

impl Display<u8> for Context {
    fn display(x: u8) -> String {
        format!("u8:{}", x)
    }
}

impl<T> Display<Vec<T>> for Context where Context: Display<T> {
    fn display(xs: Vec<T>) -> String {
        xs.into_iter().map(|x| Context::display(x) + ", ").collect()
    }
}

fn main() {
    println!("{}", Context::display(vec![1, 2, 3]));
}

这段 Rust 代码有等价的 moonbit 写法可以实现等价的抽象吗?

  1. trait 是完全单态化且静态分发。但也有动态分发的 trait object 支持,类似 Rust 的 dyn。Trait object 的相关内容可以看文档里 trait object/接口对象一节

  2. MoonBit 的 trait 解析规则是这样的:

    • 有显式实现的时候,使用显式实现,语法是 impl Trait for SelfType with method_name(...) { ... }
    • 没有显式实现的时候,fallback 到 self type 的普通方法

    fallback 到普通方法的这个行为,可以用来给已经存在的实现提供新的抽象。从整个语言生态的角度,这意味着数据结构/类型的提供者不需要依赖所有的 trait 并给它们提供实现,可以减少很多包与包之间的依赖关系,添加新的 trait 并让其他包适配也变得更简单了。如果想要更显式的行为,可以使用 impl Trait for Type with ... 的语法

  3. 支持多态的实现,比如 impl[T : Show] Show for Array[T] with ...。多态的普通方法也可以用来实现 trait。如果想要给任意类型实现,可以用默认实现,语法是 impl Trait with method(...) { ... }。除了默认实现,其他实现的 self type 必须是 toplevel type constructor,不能是类型参数

  4. 目前 MoonBit 不支持带参数的 trait,所以没办法 1:1 复刻。仅就你这个例子的话,把 T 换成 Self 可以实现类似的效果。但比如你想要每个不同的 Context 类型都有一整个 family 的不同 Display 实现的话,MoonBit 是写不出来的,需要把每个 Contextdisplay 单独定义成一个 trait

1 个赞