概念
Sized是Rust编译阶段检查对象操作的一个基本依据,Rust只允许操作已知大小的对象, 未知大小的对象只能操作它的指针(&)。
默认情况下,任何类型参数都是Sized限定的,即:
<T> 等价于 <T: Sized>
在Rust里,如果一个类型的字节大小在编译期可以确定,那么这个类型就是确定大小(Sized)的。确定类型的大小(size)对于能够在栈(stack)上为实例分配足够的空间是十分重要的。确定大小类型(sized type)可以通过传值(by value)或者传引用(by reference)的方式来传递。
如果一个类型的大小不能在编译期确定,那么它就被称为不确定大小类型(unsized type)或者DST,即动态大小类型(Dynamically-Sized Type)。因为不确定大小类型(unsized type)不能存放在栈上,所以它们只能通过传引用(by reference)的方式来传递。
示例代码
以下代码是正确的:
fn sized_correct() {
#[derive(Debug)]
struct Water<T>(T); // 等同于 struct Status<T: Sized>(T);
#[derive(Debug)]
struct Cup(Water<i32>);
let water = Water(10);
let cup = Cup(water);
println!("{:?}", cup); // output: Cup(Water(10))
}
以下代码是错误的:
fn sized_error() {
#[derive(Debug)]
struct Water<T>(T); // 等同于 struct Status<T: Sized>(T);
#[derive(Debug)]
struct Cup(Water<[i32]>); // 由于[i32] 是一个队列, 因此它是未知大小
}
为了支持参数长度可变,需要进行调整:
fn use_unsized_to_fix_sized_error() {
#[derive(Debug)]
#[allow(dead_code)]
struct Bar<T: ?Sized>(T);
#[derive(Debug)]
#[allow(dead_code)]
struct BarUse<'a>(Bar<&'a [i32]>);
}
尽管[i32]是可变长度变量,但是&[i32]是一个指针,长度是固定的,在加入?Sized
约束后,Bar
中使用到的范型可以是可变长度变量。
在Rust中,指向数组的动态大小视图(dynamically sized views)被称为切片(slice)。例如,一个&str是一个"字符串切片(string slice)" ,一个&[i32]是一个"i32切片"。切片(slice)是双宽度(double-width)的,因为他们存储了一个指向数组的指针和数组中元素的数量。
trait对象指针是双宽度(double-width)的,因为他们存储了一个指向数据的指针和一个指向vtable的指针。
不确定大小(unsized) 结构体指针是双宽度的,因为他们存储了一个指向结构体数据的指针和结构体的大小(size)。不确定大小(unsized) 结构体只能拥有有1个不确定大小(unsized)字段(field)而且它必须是结构体里的最后一个字段(field)。
总结
只有确定大小类型(sized type)的实例可以被放到栈上,也就是,可以通过值传递。
不确定大小类型(unsized type)的实例不能被放置在栈上并且必须通过引用来传递。
不确定大小类型(unsized type)的指针是双宽度(double-width)的,因为除了指向数据之外,他们还需要做一些额外的记录来追踪数据的长度或者指向一个vtable。
网友评论