Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why does Rust not recognize that a variable is a &str?

Tags:

rust

traits

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?

like image 429
vitiral Avatar asked Jun 17 '16 04:06

vitiral


People also ask

How do you pass a reference in Rust?

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 do you update a variable in Rust?

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 .


3 Answers

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.

like image 114
bluss Avatar answered Nov 02 '22 12:11

bluss


(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.

like image 38
Tatsuya Kawano Avatar answered Nov 02 '22 14:11

Tatsuya Kawano


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
like image 1
tafia Avatar answered Nov 02 '22 12:11

tafia