How to create a Vec of unit structs that implement sized trait? - struct

I want to be able to create a Vec of unit structs that implement a sized trait (preferably without using a Box<T>).
Here is what I want to be able to do, but It fails and says that ProgrammingLanguage cannot be made into a trait object:
struct Python;
struct Rust;
trait ProgrammingLanguage: Sized {
fn say_hello_world() -> ();
}
impl ProgrammingLanguage for Python {...}
impl ProgrammingLanguage for Rust {...}
// Fails because ProgrammingLanguage cannot be made into a trait object
let foo = Vec::<ProgrammingLanguage>::from([Python, Rust]);

The Sized trait would not help you here. All it can guarantee is that any particular implementation of the trait is sized, i.e. that you can not implement it on [u8] or dyn OtherTrait; but the dyn ProgrammingLanguage itself is always unsized, by definition. The reason for this is quite simple: size of the trait object is defined as the size of an original type, and there's no way to statically guarantee that every type implementing the trait would have the same size.
However, there's no real reason to avoid Box. As was mentioned in the comments, Box<UnitStruct> is essentially just a never-dereferenced dangling pointer, without any allocation at all:
struct Rust;
fn main() {
println!("{:p}", Box::new(Rust)); // prints `0x1`
}
When you convert it to the trait object, pointer itself doesn't change - compiler just adds the vtable pointer, which is necessary for any operations with trait objects anyway.

Related

How do I have a trait as a owned field of a struct?

I'm fairly new to Rust, I was tying to implement a struct that could have different structs that implemented a common trait as a field
In traditional programming languages with garbage collector I would have implemented it like this:
pub struct MemoryMapper {
regions: Vec<Region>,
}
trait MemoryMappable: Sized {
}
pub struct Region {
device: dyn MemoryMappable,
start: u32,
end: u32,
remap: bool
}
But here I have the following compilation error:
the size for values of type `(dyn MemoryMappable + 'static)` cannot be known at compilation time
the trait `Sized` is not implemented for `(dyn MemoryMappable + 'static)`
only the last field of a struct may have a dynamically sized type
change the field's type to have a statically known size
I have tried making Sized a supertrait of MemoryMappable, but it still gives me issues with the following code:
pub fn map(&mut self, device: dyn MemoryMappable, start: u32, end: u32, remap: bool) -> &Region {
self.regions.insert(0, Region{device, start, end, remap});
return self.regions.get(0).unwrap();
}
the trait `MemoryMappable` cannot be made into an object
`MemoryMappable` cannot be made into an object
Because from Rust documentation about object safety
Sized must not be a supertrait. In other words, it must not require Self: Sized.
I have no idea of how to go about this at this point
Fields of Sized structs can exist on the stack, and so must have a known size at compile time. This is what the Sized error is saying.
dyn Trait is "any type that implements Trait. So what is its size? Well, it depends on what the underlying type is.
Therefore, dyn Trait never implements Sized, even if Trait has Sized as a supertrait (all this does is guarantee that any type that implements Trait also implements Sized, but you still don't know which one it is).
In fact, making Sized a supertrait of Trait is how you opt-out of object safety, which makes it impossible to construct a dyn Trait.
Instead, the way you get around this is by using a pointer-like type:
Box<dyn Trait> creates an owned pointer
&dyn Trait creates a reference
Arc<dyn Trait> creates a reference counted pointer
etc.
The choice of pointer type depends on your use case, but in most scenarios, Box<dyn Trait> is fine, and is a good starting point.

Differences between 2 styles of default implementations in a trait?

There are 2 ways to provide methods for a Trait itself, Rustdoc distinguishes them by saying "provided methods" and impl dyn XXX. For example:
trait Trait {
fn foo(&self) {
println!("Default implementation");
}
}
impl Trait {
fn bar(&self) {
println!("Anonymous implementation?");
}
}
I noticed it when I was reading the documentation of Rust's failure crate.
What are the use cases for them? What are the differences?
The first snippet,
trait Trait {
fn foo(&self) {
println!("Default implementation");
}
}
implements a provided method on the trait. This method can be overridden by a trait implementation, but it does not have to be overridden.
The second snippet,
impl Trait {
fn bar(&self) {
println!("Anonymous implementation?");
}
}
implements an inherent method on a trait object of type dyn Trait. Method implementations for dyn Trait can only be called for trait objects, e.g. of type &dyn Trait. They can't receive self by value, since dyn Trait does not have a size known at compile time, and they can't be called on concrete types implementing Trait (including generic types with a Trait bound).
The modern notation is to write impl dyn Trait instead of impl Trait, and in fact this notation was one of the motivating examples for the introduction of the dyn keyword – the old syntax did not provide any clues as to what the semantics are, whereas the new syntax with the dyn keyword hints at the fact that this impl is only used together with dynamic dispatch.
A trait object is a fat pointer to an object implementing Trait, but the concrete type of the object is not necessarily known at compile time. The fat pointer contains a pointer to the object data, as well as a pointer to the virtual method table of the object type. The latter is used to dynamically dispatch to the correct trait implementation at runtime.
It is rather uncommon to use impl dyn Trait. Generally it's only useful if you want to make use of some dynamic type information, like downcasting to the actual type. The only traits with inherent methods on trait objects in the standard library are Any and Error.
In short: one can be overridden, and the other cannot.
When you define a trait, you define items that implementations of the trait may (or have to) override:
trait Trait {
fn foo(&self) {
println!("Default implementation");
}
}
impl Trait for i64 {
fn foo(&self) {
println!("i64 implementation: {}", self);
}
}
On the other hand, using impl Trait, you define inherent methods, which cannot be overridden:
impl Trait {
fn bar(&self) {
self.foo();
self.foo()
}
}
// Try:
impl Trait for i64 {
fn bar(&self) { ... } // error: bar cannot be overridden.
}
As a result, inherent traits methods act as the Template Method Pattern: they provide a canvas linking together one or multiple overridable method(s).
If you look at the failure crate that you linked, the method Failure::find_root_cause() states:
This is equivalent to iterating over iter_causes() and taking the last item.
You may consider those inherent methods to be convenience methods, methods providing an easy/intuitive interface for common tasks which can be accomplished manually... but are conveniently pre-defined.
Note: any inherent method could be implemented as a free function taking the trait as a first argument; however free functions cannot be called in method position.

How do I deal with wrapper type invariance in Rust?

References to wrapper types like &Rc<T> and &Box<T> are invariant in T (&Rc<T> is not a &Rc<U> even if T is a U). A concrete example of the issue (Rust Playground):
use std::rc::Rc;
use std::rc::Weak;
trait MyTrait {}
struct MyStruct {
}
impl MyTrait for MyStruct {}
fn foo(rc_trait: Weak<MyTrait>) {}
fn main() {
let a = Rc::new(MyStruct {});
foo(Rc::downgrade(&a));
}
This code results in the following error:
<anon>:15:23: 15:25 error: mismatched types:
expected `&alloc::rc::Rc<MyTrait>`,
found `&alloc::rc::Rc<MyStruct>`
Similar example (with similar error) with Box<T> (Rust Playground):
trait MyTrait {}
struct MyStruct {
}
impl MyTrait for MyStruct {}
fn foo(rc_trait: &Box<MyTrait>) {}
fn main() {
let a = Box::new(MyStruct {});
foo(&a);
}
In these cases I could of course just annotate a with the desired type, but in many cases that won't be possible because the original type is needed as well. So what do I do then?
What you see here is not related to variance and subtyping at all.
First, the most informative read on subtyping in Rust is this chapter of Nomicon. You can find there that in Rust subtyping relationship (i.e. when you can pass a value of one type to a function or a variable which expects a variable of different type) is very limited. It can only be observed when you're working with lifetimes.
For example, the following piece of code shows how exactly &Box<T> is (co)variant:
fn test<'a>(x: &'a Box<&'a i32>) {}
fn main() {
static X: i32 = 12;
let xr: &'static i32 = &X;
let xb: Box<&'static i32> = Box::new(xr); // <---- start of box lifetime
let xbr: &Box<&'static i32> = &xb;
test(xbr); // Covariance in action: since 'static is longer than or the
// same as any 'a, &Box<&'static i32> can be passed to
// a function which expects &'a Box<&'a i32>
//
// Note that it is important that both "inner" and "outer"
// references in the function signature are defined with
// the same lifetime parameter, and thus in `test(xbr)` call
// 'a gets instantiated with the lifetime associated with
// the scope I've marked with <----, but nevertheless we are
// able to pass &'static i32 as &'a i32 because the
// aforementioned scope is less than 'static, therefore any
// shared reference type with 'static lifetime is a subtype of
// a reference type with the lifetime of that scope
} // <---- end of box lifetime
This program compiles, which means that both & and Box are covariant over their respective type and lifetime parameters.
Unlike most of "conventional" OOP languages which have classes/interfaces like C++ and Java, in Rust traits do not introduce subtyping relationship. Even though, say,
trait Show {
fn show(&self) -> String;
}
highly resembles
interface Show {
String show();
}
in some language like Java, they are quite different in semantics. In Rust bare trait, when used as a type, is never a supertype of any type which implements this trait:
impl Show for i32 { ... }
// the above does not mean that i32 <: Show
Show, while being a trait, indeed can be used in type position, but it denotes a special unsized type which can only be used to form trait objects. You cannot have values of the bare trait type, therefore it does not even make sense to talk about subtyping and variance with bare trait types.
Trait objects take form of &SomeTrait or &mut SomeTrait or SmartPointer<SomeTrait>, and they can be passed around and stored in variables and they are needed to abstract away the actual implementation of the trait. However, &T where T: SomeTrait is not a subtype of &SomeTrait, and these types do not participate in variance at all.
Trait objects and regular pointers have incompatible internal structure: &T is just a regular pointer to a concrete type T, while &SomeTrait is a fat pointer which contains a pointer to the original value of a type which implements SomeTrait and also a second pointer to a vtable for the implementation of SomeTrait of the aforementioned type.
The fact that passing &T as &SomeTrait or Rc<T> as Rc<SomeTrait> works happens because Rust does automatic coercion for references and smart pointers: it is able to construct a fat pointer &SomeTrait for a regular reference &T if it knows T; this is quite natural, I believe. For instance, your example with Rc::downgrade() works because Rc::downgrade() returns a value of type Weak<MyStruct> which gets coerced to Weak<MyTrait>.
However, constructing &Box<SomeTrait> out of &Box<T> if T: SomeTrait is much more complex: for one, the compiler would need to allocate a new temporary value because Box<T> and Box<SomeTrait> has different memory representations. If you have, say, Box<Box<T>>, getting Box<Box<SomeTrait>> out of it is even more complex, because it would need creating a new allocation on the heap to store Box<SomeTrait>. Thus, there are no automatic coercions for nested references and smart pointers, and again, this is not connected with subtyping and variance at all.
In the case of Rc::downgrade this is actually just a failure of the type inference in this particular case, and will work if it is done as a separate let:
fn foo(rc_trait: Weak<MyTrait>) {}
fn main() {
let a = Rc::new(MyStruct {});
let b = Rc::downgrade(&a);
foo(b);
}
Playground
For Box<T> it is very likely you don't actually want a reference to the box as the argument, but a reference to the contents. In which case there is no invariance to deal with:
fn foo(rc_trait: &MyTrait) {}
fn main() {
let a = Box::new(MyStruct {});
foo(a.as_ref());
}
Playground
Similarly, for the case with Rc<T>, if you write a function that takes an Rc<T> you probably want a clone (i.e. a reference counted reference), and not a normal reference:
fn foo(rc_trait: Rc<MyTrait>) {}
fn main() {
let a = Rc::new(MyStruct {});
foo(a.clone());
}
Playground

Why is the `Sized` bound necessary in this trait?

I have a trait with two associated functions:
trait WithConstructor: Sized {
fn new_with_param(param: usize) -> Self;
fn new() -> Self {
Self::new_with_param(0)
}
}
Why does the default implementation of the second method (new()) force me to put the Sized bound on the type? I think it's because of the stack pointer manipulation, but I'm not sure.
If the compiler needs to know the size to allocate memory on the stack,
why does the following example not require Sized for T?
struct SimpleStruct<T> {
field: T,
}
fn main() {
let s = SimpleStruct { field: 0u32 };
}
As you probably already know, types in Rust can be sized and unsized. Unsized types, as their name suggests, do not have a size required to store values of this type which is known to the compiler. For example, [u32] is an unsized array of u32s; because the number of elements is not specified anywhere, the compiler doesn't know its size. Another example is a bare trait object type, for example, Display, when it is used directly as a type:
let x: Display = ...;
In this case, the compiler does not know which type is actually used here, it is erased, therefore it does not know the size of values of these types. The above line is not valid - you can't make a local variable without knowing its size (to allocate enough bytes on the stack), and you can't pass the value of an unsized type into a function as an argument or return it from one.
Unsized types can be used through a pointer, however, which can carry additional information - the length of available data for slices (&[u32]) or a pointer to a virtual table (Box<SomeTrait>). Because pointers always have a fixed and known size, they can be stored in local variables and be passed into or returned from functions.
Given any concrete type you can always say whether it is sized or unsized. With generics, however, a question arises - is some type parameter sized or not?
fn generic_fn<T>(x: T) -> T { ... }
If T is unsized, then such a function definition is incorrect, as you can't pass unsized values around directly. If it is sized, then all is OK.
In Rust all generic type parameters are sized by default everywhere - in functions, in structs and in traits. They have an implicit Sized bound; Sized is a trait for marking sized types:
fn generic_fn<T: Sized>(x: T) -> T { ... }
This is because in the overwhelming number of times you want your generic parameters to be sized. Sometimes, however, you'd want to opt-out of sizedness, and this can be done with ?Sized bound:
fn generic_fn<T: ?Sized>(x: &T) -> u32 { ... }
Now generic_fn can be called like generic_fn("abcde"), and T will be instantiated with str which is unsized, but that's OK - this function accepts a reference to T, so nothing bad happens.
However, there is another place where question of sizedness is important. Traits in Rust are always implemented for some type:
trait A {
fn do_something(&self);
}
struct X;
impl A for X {
fn do_something(&self) {}
}
However, this is only necessary for convenience and practicality purposes. It is possible to define traits to always take one type parameter and to not specify the type the trait is implemented for:
// this is not actual Rust but some Rust-like language
trait A<T> {
fn do_something(t: &T);
}
struct X;
impl A<X> {
fn do_something(t: &X) {}
}
That's how Haskell type classes work, and, in fact, that's how traits are actually implemented in Rust at a lower level.
Each trait in Rust has an implicit type parameter, called Self, which designates the type this trait is implemented for. It is always available in the body of the trait:
trait A {
fn do_something(t: &Self);
}
This is where the question of sizedness comes into the picture. Is the Self parameter sized?
It turns out that no, Self is not sized by default in Rust. Each trait has an implicit ?Sized bound on Self. One of the reasons this is needed because there are a lot of traits which can be implemented for unsized types and still work. For example, any trait which only contains methods which only take and return Self by reference can be implemented for unsized types. You can read more about motivation in RFC 546.
Sizedness is not an issue when you only define the signature of the trait and its methods. Because there is no actual code in these definitions, the compiler can't assume anything. However, when you start writing generic code which uses this trait, which includes default methods because they take an implicit Self parameter, you should take sizedness into account. Because Self is not sized by default, default trait methods can't return Self by value or take it as a parameter by value. Consequently, you either need to specify that Self must be sized by default:
trait A: Sized { ... }
or you can specify that a method can only be called if Self is sized:
trait WithConstructor {
fn new_with_param(param: usize) -> Self;
fn new() -> Self
where
Self: Sized,
{
Self::new_with_param(0)
}
}
Let's see what would happen if you did this with an unsized type.
new() moves the result of your new_with_param(_) method to the caller. But unless the type is sized, how many bytes should be moved? We simply cannot know. That's why move semantics require Sized types.
Note: The various Boxes have been designed to offer runtime services for exactly this problem.

How do you set the lifetime of a Rust trait?

I can set the lifetime for a Waypoint in the struct Route. In the struct AMoreDifferentRoute I use the trait Coord and get the error
explicit lifetime bound required
How do you set the lifetime of a trait in this case?
extern crate collections;
use super::wp;
use coord::Coord;
pub struct Route<'a> {
waypoints: &'a Vec<wp::Waypoint>
}
pub struct AMoreDifferentRoute<'a> {
waypoints: &'a Vec<Coord>
}
You write it as an additional trait bound:
pub struct AMoreDifferentRoute<'a> { // '
waypoints: &'a Vec<Box<Coord+'a>>
}
You need to specify a lifetime in Box<Coord+'a> because the trait can be implemented for a struct which has a lifetime parameter itself, so there should be a way to specify this lifetime parameter even if the actual struct type is hidden behind a trait object.
Moreover, you can't have bare Coord as its size is unknown, because Vec needs to know its components size to lay them out in memory properly. Hence you need some kind of wrapper to store trait objects. Box will do nicely.
As Coord is a trait, you need to box the value in some way as a trait object, such as Vec<Box<Coord>>.
You should also strongly consider whether this is actually what you want anyway; very often it’s not. But to make any sort of judgement on that, I’d need to see more code.

Resources