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
?
Syntax. An ampersand ( & ) is used with a variable's name while passing its reference instead of its value to a function. The function's signature should also have an ampersand with the type of argument that receives the reference. When a function takes in a reference as a parameter, it is called borrowing.
How to change variable values. By default, variables in Rust are immutable. This means that once a value is assigned to a variable, it cannot be changed. However, if we want to enable a variable to be mutable, we can specify it in its initialization by writing the keyword mut between the name and the keyword let .
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
.
(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.
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
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With