Rust 为什么对取消引用的trait对象或切片调用方法会编译?

Rust 为什么对取消引用的trait对象或切片调用方法会编译?,rust,traits,dereference,Rust,Traits,Dereference,给定以下代码: 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: &

给定以下代码:

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给出了一个切片,但是切片没有大小,所以我也不清楚它是如何编译的。如果我取消对第二条语句的注释,我将得到

|设s:&[i32]=*v; | ^^ | | |应为&[i32],找到切片 帮助:考虑在这里借用:'&v ' | =注意:预期类型为`&[i32]` 找到类型“[{integer}]`
预期的类型和[i32]是否意味着预期的切片参考?

根据第一段代码产生的MIR判断,*f.filter相当于f.filter;编译器似乎意识到,由于filter是一个on&self方法,所以取消对它的引用没有任何作用,因此完全忽略了它


然而,第二种情况则不同,因为取消对切片的引用会引入边界检查代码。在我看来,编译器还应该能够判断此操作取消引用不会引入任何有意义的更改和/或不会出现越界错误,并将其视为常规切片索引,但这背后可能有某种原因。

根据第一段代码生成的MIR判断,*f.过滤器相当于f.过滤器;编译器似乎意识到,由于filter是一个on&self方法,所以取消对它的引用没有任何作用,因此完全忽略了它


然而,第二种情况则不同,因为取消对切片的引用会引入边界检查代码。在我看来,编译器还应该能够判断此操作取消引用不会引入任何有意义的更改和/或不会出现越界错误,并将其视为常规切片索引,但这背后可能有某种原因。

取消引用特征对象没有问题。事实上,它必须在某一点上被取消引用,否则它将毫无用处

设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、 过滤器;由于临时取消引用的对象未放在堆栈上,因此可以正常工作。事实上,这与f.filter相同。Rust会根据需要自动应用尽可能多的解引用以访问实际对象。这是有案可查的


在第二种情况下发生的是对切片的访问,因此它免费为切片实现所有方法*v提供一个取消引用的切片,您可以将其指定给切片。这是一个明显的类型错误。

取消引用trait对象没有问题。事实上,它必须在某一点上被取消引用,否则它将毫无用处

设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、 过滤器;由于临时取消引用的对象未放在堆栈上,因此可以正常工作。事实上,这与f.filter相同。Rust会根据需要自动应用尽可能多的解引用以访问实际对象。这是有案可查的


在第二种情况下发生的是对切片的访问,因此它免费为切片实现所有方法*v提供一个取消引用的切片,您可以将其指定给切片。这是一个明显的类型错误。

&[T]是一个切片;[T] 是一个数组。&[T]是一个切片;[T] 是一个数组。也@ljedrz谢谢!如果self是通过value接收的,而I执行f.filter,则会出现2个错误。首先,无法移动Function类型的值:无法静态确定函数的大小,其次,无法移出借用的内容。第二个错误是显而易见的。第一,我不能完全理解为什么它抱怨功能的大小而不是自我的大小。请您详细解释一下,为什么它(即f.filter)可以与接收器self一起作为参考。@soupybionics虽然这或多或少是一个不同的问题,但同样的答案适用。如果您有fn filterself,编译器只知道类型Self实现函数。它不知道self的大小,因为任何类型都可能隐藏在f:&函数后面。如果您尝试使用k.filter,它应该可以工作,因为k:renty的大小在编译时是已知的
其他变量,比如让k1=*f,将其作为参数移动到函数中,基本上是一样的。谢谢!这很有帮助。同样@ljedrz谢谢!如果self是通过value接收的,而I执行f.filter,则会出现2个错误。首先,无法移动Function类型的值:无法静态确定函数的大小,其次,无法移出借用的内容。第二个错误是显而易见的。第一,我不能完全理解为什么它抱怨功能的大小而不是自我的大小。请您详细解释一下,为什么它(即f.filter)可以与接收器self一起作为参考。@soupybionics虽然这或多或少是一个不同的问题,但同样的答案适用。如果您有fn filterself,编译器只知道类型Self实现函数。它不知道self的大小,因为任何类型都可能隐藏在f:&函数后面。如果您尝试使用k.filter,它应该可以工作,因为k:renty的大小在编译时是已知的。或者简单地说,将一个值移动到另一个变量中,如let k1=*f,并将其作为参数移动到函数中基本上是一样的。谢谢!这很有帮助。