Rust Trait Object 和对象安全

所谓 trait 对象,可以理解为其它面向对象语言中,指向接口或基类的指针或引用。其它OO语言指向基类的指针在运行时确定其实际类型。
Rust 没有类继承,指向 trait 的指针或引用起到类似的效果,运行时被确定具体类型。所以编译期间不知道大小。
trait 对象体现出来就是对一个 trait类型的指针或引用。

不是所有的 trait 都可以被当做 trait 对象使用,能作为 Trait 对象使用的 trait 要同时满足以下规则:

     rule 1: trait 的 Self 类型参数不能被限定为 Sized (Trait 不能标记为 Sized )
and  rule 2: trait 中的所有方法必须是 【对象安全】的
        该规则要求方法满足以下条之一
      or   rule 2.1: 方法受 Self: Sized 约束(被 Sized 约束的方法不能被动态调用,所以能超脱 rule 2.2 的限制)
      or   rule 2.2: 方法签名同时满足以下三点【总结:没有额外的 Self 类型参数的非泛型成员方法】
         and    rule 2.2.1 必须不包含任何泛型参数
         and    rule 2.2.2 第一个参数必须为 Self 类型,或者可以解引用为 Self 类型
         and    rule 2.2.3 Self 不能出现在除第一个参数之外的地方,包括返回值
and  rule 3: Trait 中不能包含关联常量(Associated Constant)

rule 2.1: "方法受 Self: Sized 约束" 实际上是告诉编译器这个方法不会被动态调用。这样编译器就可以忽略对这个方法的检查。Sized Trait 是让整个 Trait 没法动态调用,对 方法的Sized 只限定这个方法不行,这样让其他部分还是可以动态调用。相当于把约束范围缩小了。

很多文章对都对这些规则有描述,但是常常把 rule3 “Trait 中不能包含关联常量”作为“对象安全的方法”,逻辑不对。这一条应该是一个顶级规则。

下面的示例代码对上面所有规则都做了演示,会导致编译错误的代码放到了 注释中: 

// 可以转成 Trait 对象
trait Good {
    fn foo(&self);
}
fn use_good(_: &dyn Good) -> (){}
//-------------------------------------------

trait BadRule3 {
    const S :i32 = 100;
    fn foo(&self);
}
// 错误信息:this trait cannot be made into an object. because it contains this associated `const`
//fn use_bad_rule3(_: &dyn BadRule3) -> (){}

//-------------------------------------------
trait BadRule1:Sized{
    fn foo(&self);
}
//  错误信息: .because BadRule1 requires `Self: Sized,  this trait cannot be made into an object...
//fn use_bad_rule1(_: &dyn FooGood) -> (){}

//-------------------------------------------
trait BadRule221{
    fn foo(&self,t:T);
}
// 错误信息:this trait cannot be made into an object...,.because method `foo` has generic type parameters
//fn use_foo_bad_rule221(_: &dyn BadRule221) -> (){}

//-------------------------------------------
trait BadRule222{
    fn foo();
}
// 错误信息:this trait cannot be made into an object...,.because associated function `foo` has no `self` parameter
//fn use_foo_bad_rule221(_: &dyn BadRule222) -> (){}

//-------------------------------------------
trait BadRule223{
    fn foo(&self) -> Self;
}
// 错误信息:this trait cannot be made into an object...,.because method `foo` references the `Self` type in its return type
//fn use_foo_bad_rule221(_: &dyn BadRule223) -> (){}

//-------------------------------------------
// 可以转换为 Trait 对象,虽然使用泛型方法违反了 rule 2.2.1,但是 方法的 Self:Sized 满足了 rule 2.1
trait GoodRule21a{
    fn foo(&self,t:T) where Self:Sized;
}
fn use_foo_good_rule21(_: &dyn GoodRule21a) -> (){}
//-------------------------------------------

// 可以转换为 Trait 对象,虽然方法同时违反了 rule 2.2 包含的三条规则,但是 方法的 Self:Sized 满足了 rule 2.1
// use_foo_good_rule21b 函数使用 GoodRule21b 作为 Trait 对象是可以编译通过,但 foo2 方法受 rule 2.1 的约束是不能被动态调用的
trait GoodRule21b{
    fn foo(t:T) -> Self where Self:Sized;
    fn foo2(&self,i:i32) -> Self where Self:Sized;
}
fn use_foo_good_rule21b(obj: &dyn GoodRule21b) -> (){

    // error: the `foo2` method cannot be invoked on a trait object
    // obj.foo2(3);
}
//-------------------------------------------

fn main(){}

 

你可能感兴趣的:(rust)