Rust如何处理将&SizedType转换为&UnsizedType的问题?

时间:2019-05-05 03:22:47

标签: rust

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>这样的其他未定义大小的类型?

1 个答案:

答案 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>的安全方法。

Code on the playground