首页 文章

为什么在解引用的特征对象或切片上调用方法?

提问于
浏览
1

给出以下代码:

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] 是指"expected a reference of slice"?

2 回答

  • 1

    取消引用特征对象是没有问题的 . 事实上,它必须在某个时候被取消引用,否则它将毫无用处 .

    let k1 = (*f); 失败不是因为解除引用,而是因为你试图将原始特征对象放在堆栈上(这是局部变量存在的地方) . 堆栈上的值必须具有编译时已知的大小,而特征对象不是这种情况,因为任何类型都可以实现特征 .

    下面是一个示例,其中具有不同大小的结构实现了特征:

    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会自动应用所需数量的解引用来获取实际对象 . 记录in the book .

    在第二种情况下发生的是切片的Vec implements Deref,因此它可以免费获得为切片实现的所有方法 . *v 为您提供了一个解除引用的切片,您可以将其指定给切片 . 这是一个明显的类型错误 .

  • 3

    根据第一段代码生成的MIR判断, (*f).filter() 相当于 f.filter() ;似乎编译器知道由于 filter&self 上的一个方法,因此取消引用它不起任何作用,并且完全省略 .

    但是,第二种情况不同,因为解引用切片会引入边界检查代码 . 在我看来,编译器还应该能够告诉该操作(解除引用)不会引入任何有意义的更改(和/或不会出现越界错误)并将其视为常规切片索引,但这背后可能有一些原因 .

相关问题