Welcome toVigges Developer Community-Open, Learning,Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
1.3k views
in Technique[技术] by (71.8m points)

rust - Understanding Traits and Object Safety

I am struggling with the basics of object safety. If I have this code

struct S {
    x: i32,
}

trait Trait: Sized {
    fn f(&self) -> i32
    where
        Self: Sized;
}

fn object_safety_dynamic(x: Trait) {}

I receive

error[E0038]: the trait `Trait` cannot be made into an object
  --> src/lib.rs:11:29
   |
5  | trait Trait: Sized {
   |       -----  ----- ...because it requires `Self: Sized`
   |       |
   |       this trait cannot be made into an object...
...
11 | fn object_safety_dynamic(x: Trait) {}
   |                             ^^^^^ the trait `Trait` cannot be made into an object

When I add or remove : Sized as the supertrait or as f's bound, I receive slightly different error messages.

Could someone explain:

  • Why does this particular example not work? The chapter Trait Objects states:

    So what makes a method object-safe? Each method must require that Self: Sized

    Isn't that fulfilled?

  • What is the difference between Trait: Sized and where Self: Sized? (Well, yes, one inherits the trait and the other one is a parameter bound, but from Rust's trait object perspective?

  • What is the preferred change I had to make object_safety_dynamic work?

I am using rustc 1.19.0-nightly (01951a61a 2017-05-20) if it matters.

Addressing the comment on fixed sizes.

trait TraitB {
    fn f(&self) -> i32
    where
        Self: Sized;

    fn g<T>(&self, t: T) -> i32
    where
        Self: Sized;
}
See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Answer

0 votes
by (71.8m points)

Why does this particular example not work? The chapter Trait Objects states:

So what makes a method object-safe? Each method must require that Self: Sized

Isn't that fulfilled?

This question really is: What is a trait object?

A trait object is an interface in the Object-Oriented paradigm:

  • it exposes a limited set of methods,
  • which are applied to an unknown concrete type.

The fact that the concrete type to which the operations is applied is unknown is specifically why one uses a trait object, as it allows manipulating a heterogeneous set of types in a uniform fashion down to the assembly level.

The fact the concrete type is unknown, however, means that the size of the memory area which contains the memory is also unknown; therefore a trait object can only be manipulated behind a reference or pointer such as &dyn TraitObject, &mut dyn TraitObject or Box<dyn TraitObject> for example.

At the memory level, each of them is represented identically:

  • a pointer to a virtual table, which is a structure holding one function pointer per "method" of the trait object at a fixed offset,
  • a pointer to the actual data of the object.

What is the difference between Trait: Sized and where Self: Sized? (Well, yes, one inherits the trait the other one is a parameter bound, but from Rust's trait object perspective?)

There is no inheritance in Rust. In both cases those are bounds:

  • Trait: Sized states that the trait itself can only be implemented for a type that already implements Sized,
  • fn method(&self) where Self: Sized states that only types that implement Sized can implement this method.

Note: when implementing a trait, all methods must end up having a definition; the latter is therefore only really useful if a default implementation is provided for the method with the Self: Sized bound, as is shown here.

What is the preferred change I had to make object_safety_dynamic work?

You have to take the trait object by reference or pointer. Whether you use a reference or pointer depends on whether you want to transfer ownership or not.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome to Vigges Developer Community for programmer and developer-Open, Learning and Share
...