以下代码编译并运行良好:
use std::fmt::Display;
fn display(x: &str) {
println!("{}", x);
}
fn main() {
let s: &str = "hi there";
display(s);
}
但是,如果您将display
功能更改为
fn display(x: &Display)
它出现以下错误:
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);
将display(s)
更改为display(&s)
它再次有效。
这里发生了什么?显然类型是&str
,但当&Display
是输入参数时,它不会识别出来。
注意:&34
也可以作为参数使用。这是因为Display
实际上是为&str
实现的,而不是str
?
答案 0 :(得分:6)
您正在请求将&str
强制转换为&Display
(对特征对象的引用),这似乎有意义,因为类型str
实现了Display
。< / p>
然而,从Rust 1.9开始(当前没有更改此计划),如果类型&T
为“{&Trait
,则T
到Sized
只能转换为特征对象。 {1}}”。
原因是实施。像&Display
这样的特征对象由两个字段组成,一个指向数据的指针,以及一个指向特征方法表(vtable)的指针。此表示仅适用于其引用为“thin”的值,这些值恰好是类型where T: Sized
。 &str
是一个“胖”引用,它有一个指针和一个长度,因此str
不能是特征对象的数据。
为什么显示器(&amp; s)有效?我想这是对“肥胖”参考的引用?
是的,确切地说。 &&str
是一个“瘦”引用,指向具有&str
值的变量。所以它可以转换为&Display
。
答案 1 :(得分:5)
(推荐我对主要答案的编辑)
当您编写fn display(x: &Display)
时,该函数会通过解除引用来获取可以强制转换为trait object的值。 Rust函数也需要在编译时知道参数x
的值大小。
当&34
(&u32
类型)被取消引用并强制转换为特质对象时,它变为u32
并且可以确定其大小。
取消引用&str
时,它变为str
并且无法确定其大小,因为字符串的长度可以是任何值。
将&
添加到&str
(&&str
),将其取消引用回&str
,这是一个指针,可以确定其大小。我相信这就是编译器只在您的代码中接受display(&s)
的原因。
答案 2 :(得分:1)
通过将显示更改为显示(&amp; s),它再次有效。
这一切都归结为&str: Display (+ Sized)
和str: !Display
(这只是一种表示法)
display(s)
预计s: &Display
,=&gt; &str: &Display
这是假的。display(&s)
期待&s: &Display
=&gt; &(&str) : &Display
,&str: Display