文章

Rust中?Sized使用方式和场景应用

首先 大部分的类型是编译期间就能确定类型的长度的 比如i32 i64 这种,即便是复合类型 Struct如果内部的都是明确的固定类型的话,本身也是可以确定长度的。 因此这些都是Sized . 但是在Rust代码中还有一类是无法明确的在编译期间知道其长度,这类称之为DST(dynamic sized type) , 比如 trait对象 和slices数组 。

对于指向DST类型的指针,本身需要两倍的存储空间:

  • 指向slice数组的 也需要存储额外的slice的数量信息
  • 指向trait的 也需要存储一个指向vtable的指针
1
2
3
4
5
6
7
8
9
10
11
12
13
14
use std::mem::size_of;
use std::boxed::Box;
use std::string::ToString;

#[test]
fn test_sized_type(){
    let single_pointer_size = size_of::<&()>();
    let double_pointer_size = 2 * single_pointer_size;

    assert_eq!(double_pointer_size, size_of::<&str>());
    assert_eq!(double_pointer_size, size_of::<&[i32]>());
    assert_eq!(double_pointer_size, size_of::<Box<dyn ToString>>());
    assert_eq!(double_pointer_size, size_of::<&dyn ToString>());
}

如果传入参数的时候需要上述的DST类型,可以使用?Sized 标示出来, 不标识的话默认所有的都是Sized。 默认情况下 泛型类型都是约束Sized类型,因此必须传递编译期间已知类型。同时Rust中大部分的trait都是作为约束来限制传输的参数类型的, 但是?Sized则扩大了范围,使用这种作为trait 约束,可以传递几乎任何类型的对象。

比如下面的例子中:

  • 定义一个FooSized的结构体,内部仅存储一个引用类型
  • 定义一个trait Print 实现类型的打印
  • 定义FooSized来实现trait Print
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
  struct FooSized<'a, T>(&'a T) where T: 'a;

    trait Print{
        fn print(&self);
    }

    impl<'a, T> Print for FooSized<'a, T> where T: 'a + fmt::Display {
        fn print(&self) {
            println!("{}", self.0)
        }
    }

    #[test]
    fn test_un_sized_type(){
        let h = FooSized("hello");
        h.print();
    }

当我们尝试执行print的时候发现会报错,尽管我们传递的是一个引用,但是这个引用是一个&‘staic str, 非固定大小的类型, 因此我们必须使用提供DST的类型约束。

image.png

修改上述的约束条件:

1
2
3
4
5
6
7
8
9
10
11
    struct FooSized<'a, T: ?Sized>(&'a T) where T: 'a;

    trait Print{
        fn print(&self);
    }

    impl<'a, T:?Sized> Print for FooSized<'a, T> where T: 'a + fmt::Display {
        fn print(&self) {
            println!("{}", self.0)
        }
    }
本文由作者按照 CC BY 4.0 进行授权

热门标签