Function object_safety

Source
pub fn object_safety()
Expand description

§对象安全

§对象安全

一个 trait 如果能实现自己,就认为它是对象安全的

为什么必须是对象安全呢?

trait对象,在运行时已经擦除了类型信息,要通过虚表调用相应的方法。不像静态分发那样,trait对象不是为每个类型都实现trait的方法,而是只实现一个副本(自动为其实现自身),结合虚函数去调用。

现在想一个问题: 假如那个类型没有实现这个方法怎么办? 实际上,会有很多种情况下,会出现这个问题。运行时确定的类型和方法应该合法的,保证trait对象在运行时可以安全地调用相关的方法。

比如trait里有泛型函数。这就搞的很复杂了,可能运行时无法确定该调用哪个函数。反正是各种情况吧。所以,为了避免出现这种问题,官方引入了对象安全的概念。 实际上就是引入了一系列的规则,也就是上面列出的那些。编译器根据这些规则,在编译期判断一个你写的trait对象,是不是合法的。

比如:trait对象其实在内部维护两个表:safe_vtable和nonself_vtable,标记有where Self: Sized的会被归类到nonself_vtable,也就是说,不会被trait对象调用。 这样的话,方法标记有where Self: Sized的trait对象自然是安全的,因为这表示 这个方法 只能为 Self: Sized 都类型实现,是有条件的,所以在运行时有可能存在无效(万一有不是Sized的类型调用,就没有该方法)调用。

如果是合法的,则代表了,这个trait对象在运行时调用方法应该是没问题的。 不会出现没有实现,或者不知道该调用哪个的情况。 这就是对象安全的概念。它和内存安全并无直接关系。 所以,对象安全的本质就是为了让trait对象可以安全地调用相应的方法。

如果没有Sized的限定,那么就会很容易写出无用的类型。比如 Box,它用做trait对象即便会编译,但是不能用它做任何事情(后面有演示代码)。 对于更复杂的trait,往往就没有这么明显了,只有在做了大量繁重的工作之后可能会突然发现某个trait对象无法正常调用方法。 所以,为trait增加Sized限定,然后编译器自动为该trait实现自身,就可以在编译期准确排除无效的trait对象。 这就是对象安全。需要注意的是,对象安全和内存安全并无直接的关联,它只是保证trait对象在运行时可以安全准确地调用相关的方法。

    trait StarkFamily {
        fn last_name(&self)  -> &'static str;
        fn totem(&self) -> &'static str;
    }

    trait TullyFamily {
        fn territory(&self) -> &'static str;
    }

    trait Children {
        fn new(first_name: &'static str) -> Self where Self: Sized;
        fn first_name(&self) -> &'static str;
    }

    impl StarkFamily for Children {
        fn last_name(&self)  -> &'static str{
            "Stark"
        }

        fn totem(&self)  -> &'static str{
            "Wolf"
        }
    }

    impl TullyFamily for Children {
        fn territory(&self)  -> &'static str{
            "Riverrun City"
        }
    }

    struct People{
        first_name: &'static str
    }

    impl Children for People {
        fn new(first_name: &'static str) -> Self where Self: Sized{
            println!("hello : {} Stark ", first_name);
            People{first_name: first_name}
        }

        fn first_name(&self) -> &'static str{
            self.first_name
        }
    }

    fn full_name(person: Box<dyn Children>) {
        println!(" --- Winter is coming, the lone {:?} dies, the pack lives ---", person.totem());
        let full = format!("{} {}", person.first_name(), person.last_name() );
        println!("I'm {:?}", full );
        println!("My Mother come from {:?}", person.territory());
    }

    fn main() {
        let sansa = People::new("Sansa");
        let aray = People::new("Aray");

        let starks: Box<dyn Children> = Box::new(sansa);
        full_name(starks);

        let starks: Box<dyn Children> = Box::new(aray);
        full_name(starks);
    }

对象安全规则 Rust 源码:https://github.com/rust-lang/rust/blob/941343e0871dd04ea774e8cee7755461b144ef29/compiler/rustc_middle/src/traits/mod.rs#L643