4

In Rust for Rustaceans, Jon Gjengset states (emphasis mine):

When you’re given the choice between static and dynamic dispatch, there is rarely a clear-cut right answer. Broadly speaking, though, you’ll want to use static dispatch in your libraries and dynamic dispatch in your binaries. In a library, you want to allow your users to decide what kind of dispatch is best for them, since you don’t know what their needs are. If you use dynamic dispatch, they’re forced to do the same, whereas if you use static dispatch, they can choose whether to use dynamic dispatch or not."

My interpretation of this was:

  • In a library one should use: fn execute(x: Arc<impl MyTrait>) -> ... This is static dispatch.

  • In a binary: let x: Arc<dyn MyTrait> = Arc::new(my_obj) This is dynamic.

Then I want to call execute(x), but the compiler complains:

error[E0277]: the size for values of type `dyn MyTrait` cannot be known at compilation time
  --> src/main.rs:12:13
   |
12 |     execute(x);
   |     ------- ^ doesn't have a size known at compile-time
   |     |
   |     required by a bound introduced by this call
   |
   = help: the trait `Sized` is not implemented for `dyn MyTrait`

What am I missing?

Update: One of MyTrait's methods is: fn build(conf: MyStruct) -> Self where Self: Sized; where I put Sized in order for this method to be "skipped". In other words, this method would never be called from execute function.

kmdreko
  • 42,554
  • 6
  • 57
  • 106
Anatoly Bugakov
  • 772
  • 1
  • 7
  • 18

1 Answers1

4

Your interpretation is correct. However, every generic argument in Rust is, unless explicitly stated otherwise, assumed to be Sized. So when you write

fn execute(x: Arc<impl MyTrait>) {}

That's equivalent[1] to

fn execute<T>(x: Arc<T>)
  where T: MyTrait {}

And since we didn't opt out of the Sized constraint, this gives

fn execute<T>(x: Arc<T>)
  where T: MyTrait + Sized {}

And while dyn MyTrait absolutely does implement MyTrait, it does not implement Sized. So if we write any of the following

fn execute(x: Arc<impl MyTrait + ?Sized>) {}
fn execute<T: MyTrait + ?Sized>(x: Arc<T>) {}
fn execute<T>(x: Arc<T>) where T: MyTrait + ?Sized {}

Then Rust will accept a trait object as argument to this function.


[1] Almost equivalent. You can't write execute::<MyType>() if you're using the impl syntax.

Silvio Mayolo
  • 62,821
  • 6
  • 74
  • 116