6

The below code compiles and runs fine:

use std::fmt::Display;

fn display(x: &str) {
    println!("{}", x);
}

fn main() {
    let s: &str = "hi there";
    display(s);
}

However, if you change the display function to be

fn display(x: &Display)

It gives the following error:

src/main.rs:9:13: 9:14 error: the trait `core::marker::Sized` is not implemented for the type `str` [E0277]
src/main.rs:9     display(s);

By changing display(s) to display(&s) it works again.

What is going on here? Clearly the type is &str, but when &Display is the input argument it doesn't recognize that.

Note: &34 also works as an argument just fine. Is this because Display is actually implemented for &str and not str?

Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
vitiral
  • 8,446
  • 8
  • 29
  • 43

3 Answers3

7

You are requesting that &str should be coerced to &Display (a reference to trait object), which seems to make sense since the type str implements Display.

Yet as of Rust 1.9 (and no current plans of changing this), the conversion to a trait object is only possible for &T to &Trait if the type T is “Sized”.

The reason is the implementation. A trait object like &Display consists of two fields, one pointer to the data, and one pointer to the table of trait methods (vtable). This representation is only possible for values whose references are “thin” which are exactly the types where T: Sized. A &str is a “fat” reference, it has a pointer and a length, and so a str can not be a trait object's data.


Why does display(&s) work though? I guess that is a referene to the "fat" reference?

Yes, exactly. A &&str, is a ”thin” reference that points to the variable with the &str value. So it can be converted to &Display.

bluss
  • 12,472
  • 1
  • 49
  • 48
  • 1
    Why does `display(&s)` work though? I guess that is a referene to the "fat" reference? Are there other types that are "fat" referenes, or is `&str` special somehow? – vitiral Jun 17 '16 at 15:45
  • 1
    I guess I don't understand why `str` isn't just a struct with the data and the length, instead of `&str`? Then `&str` would no longer be a fat pointer and we wouldn't have this problem! – vitiral Jun 17 '16 at 15:46
  • 1
    I looked up fat pointers and this was a pretty good reference: https://www.reddit.com/r/rust/comments/38tv6n/when_is_a_reference_not_a_pointer/ I'll keep investigating... – vitiral Jun 17 '16 at 15:53
  • 1
    this was also a good reference: http://stackoverflow.com/questions/31949579/understanding-pointer-types-in-rust – vitiral Jun 17 '16 at 15:55
5

(Promoted my edits to the main answer)

When you write fn display(x: &Display), the function takes a value that can be coerced into a trait object by dereferencing it. Also Rust function requires the value size of the parameter x to be known at the compile time.

When &34 (&u32 type) is dereferenced and coerced into trait object, it becomes u32 and its size can be determined.

When &str is dereferenced, it becomes str and its size cannot be determined as the length of a string can be anything.

By adding & to &str (&&str), it is dereferenced back to &str, which is a pointer and its size can be determined. I believe this is why the compiler only accepts display(&s) in your code.

Tatsuya Kawano
  • 151
  • 1
  • 5
  • This.. its coersion happening. Perhaps promote your edit into the main answer? – Simon Whitehead Jun 17 '16 at 06:48
  • Thank you for the suggestion. I promoted my edit (with some extra modifications for clarity) into the main answer. – Tatsuya Kawano Jun 17 '16 at 08:40
  • The book says "A trait is object-safe if both of these are true: 1) trait does not require that `Self: Sized`; 2) all of its methods are object-safe". However only reference to `Sized` value can be casted to trait object – aSpex Jun 17 '16 at 10:28
  • I would add to this answer (from my reading after getting help from everyone) that `str` is really just a wrapper for `[u8]`, who's size cannot be known at compile time. Thus, the "fat" reference. – vitiral Jun 17 '16 at 15:57
1

By changing display(s) to display(&s) it works again.

It all goes down to &str: Display (+ Sized) and str: !Display (this is just a notation)

  • display(s) expects s: &Display, => &str: &Display which is false.
  • display(&s) expects &s: &Display => &(&str) : &Display which is true as &str: Display
tafia
  • 1,512
  • 9
  • 18
  • `Display` implemented for `str`, not for `&str`. http://doc.rust-lang.org/src/core/up/src/libcore/fmt/mod.rs.html#1371-1375 – aSpex Jun 17 '16 at 09:40