给定以下代码:
trait Function {
fn filter (&self);
}
#[derive(Debug, Copy, Clone)]
struct Kidney {}
impl Function for Kidney {
fn filter (&self) {
println!("filtered");
}
}
fn main() {
let k = Kidney {};
let f: &Function = &k;
//let k1 = (*f); //--> This gives a "size not satisfied" error
(*f).filter(); //--> Works; what exactly happens here?
}
我不确定它为什么编译。我预计最后一句话会失败。我想我在学习 Rust 时忽略了一些基础知识,因为我无法理解为什么取消引用一个特征(存在于指针后面)应该编译。
此问题是否类似于以下情况?
let v = vec![1, 2, 3, 4];
//let s: &[i32] = *v;
println!("{}", (*v)[0]);
*v
给出了一个切片,但切片没有大小,所以我也不清楚它是如何编译的。如果我取消注释我得到的第二条语句
| let s:&[i32]= *v;
| ^^
| |
| expected &[i32], found slice
| help: consider borrowing here: `&*v`
|
= note: expected type `&[i32]`
found type `[{integer}]`
expected type &[i32]
的意思是"期望切片的参考"吗?
取消引用特征对象是没有问题的。事实上,它必须在某个时候被取消引用,否则它将毫无用处。
let k1 = (*f);
失败不是因为取消引用,而是因为您尝试将原始 trait 对象放在堆栈上(这是局部变量所在的位置)。堆栈上的值必须具有编译时已知的大小,而 trait 对象则不是这种情况,因为任何类型都可以实现 trait。
下面是具有不同大小的结构实现特征的示例:
trait Function {
fn filter (&self);
}
#[derive(Debug, Copy, Clone)]
struct Kidney {}
impl Function for Kidney {
fn filter (&self) {
println!("filtered");
}
}
#[derive(Debug, Copy, Clone)]
struct Liver {
size: f32
}
impl Function for Liver {
fn filter (&self) {
println!("filtered too!");
}
}
fn main() {
let k = Kidney {};
let l = Liver {size: 1.0};
let f: &Function;
if true {
f = &k;
} else {
f = &l;
}
// Now what is the size of *f - Kidney (0 bytes) or Liver (4 bytes)?
}
(*f).filter();
工作是因为临时取消引用的对象不会放在堆栈上。其实这和f.filter()
一样。Rust 会自动根据需要应用任意数量的取消引用来获取实际对象。这在书中有记载。
在第二种情况下发生的情况是,Vec
实现了对切片Deref
,因此它免费为切片实现了所有方法。*v
为您提供一个取消引用的切片,您可以将其分配给切片。这是一个明显的类型错误。
从第一段代码产生的 MIR 来看,(*f).filter()
等同于f.filter()
;编译器似乎知道,由于filter
是&self
上的一种方法,取消引用它没有任何用处,完全省略。
但是,第二种情况有所不同,因为取消引用切片会引入边界检查代码。在我看来,编译器还应该能够判断此操作(取消引用)不会引入任何有意义的更改(和/或不会有越界错误)并将其视为常规切片索引,但这背后可能有一些原因。