struct MaybeSized<T: ?Sized> {
v: T,
}
fn main() {
let sized = MaybeSized {
v: "sized".to_string(),
};
use std::fmt::Display;
{
// what exactly happens here?
let ref1: &MaybeSized<Display> = &sized;
}
{
// why this fails to compile?
let ref2: &MaybeSized<str> = &sized;
}
}
MaybeSize<String>
是大小类型;创建ref1 : &MaybeSized<Display>
)时堆栈和堆上的内容是什么?为什么这种“魔术”不适用于像MaybeSized<str>
这样的其他未定义大小的类型?
答案 0 :(得分:2)
从&MaybeSized<String>
到&MaybeSized<dyn Display>
的转换称为unsized coercion。可以将具体类型强制转换为它们实现的特征的特征对象,并且这种强制在特定条件下扩展到通用结构:
Foo<..., T, ...>
到Foo<..., U, ...>
,当:
Foo
是一个结构。T
实现Unsize<U>
。Foo
的最后一个字段的类型涉及T
。- 如果该字段的类型为
Bar<T>
,则Bar<T>
实现Unsized<Bar<U>>
。T
不属于任何其他字段的类型。
(有关详细信息,请单击上面的语言参考的链接。)
变量sized
存储在堆栈上,但是String
的数据分配在堆上。引用ref1
作为 fat指针存储在堆栈中–指向sized
的指针以及指向String as dyn Display
的虚拟方法表的指针,以动态允许必要时调用正确的方法。
这不适用于MaybeSized<str>
,因为没有大小不等的强制转换为str
。您可以使用 deref强制将&String
转换为&str
,但这不是我们所需要的–调整大小的类型需要强制的大小。未调整大小的类型MaybeSized<str>
由实际字符串数据组成,而MaybeSized<String>
由长度,容量和指向堆的指针组成,因此没有办法使内存布局匹配。
尽管如此,还有其他一些情况,例如
let a = MaybeSized { v: [65u8, 66, 67]};
let b: &MaybeSized<[u8]> = &a;
可以正常工作,因为从[u8; 3]
到[u8]
的强制大小不一。如果确实要使用不安全的代码,可以将其转换为&MaybeSized<str>
:
let c: &MaybeSized<str> = unsafe { &*(b as *const _ as *const _) };
我想不出一种创建&MaybeSized<str>
的安全方法。