rust 为什么Deref::deref的返回类型本身是一个引用?

ego6inou  于 12个月前  发布在  其他
关注(0)|答案(3)|浏览(119)

我在阅读Rust的Deref trait的文档:

pub trait Deref {
    type Target: ?Sized;
    fn deref(&self) -> &Self::Target;
}

字符串
deref函数的类型签名对我来说似乎是违反直觉的;为什么返回类型是引用?如果引用实现了这个trait,那么它们可以被解引用,这会有什么影响呢?
我能想到的唯一解释是引用并没有实现Deref,而是被认为是“可重复解引用的”。然而,如何编写一个多态函数,它将适用于 * 任何 * 可解引用的类型,包括Deref<T>&T

hwamh0ep

hwamh0ep1#

引用不实现Deref
你可以看到all the types that implement Deref&T在这个列表中:

impl<'a, T> Deref for &'a T where T: ?Sized

字符串
不明显的是,当您将*运算符与实现Deref的东西一起使用时,会应用语法糖。看看这个小例子:

use std::ops::Deref;

fn main() {
    let s: String = "hello".into();
    let _: () = Deref::deref(&s);
    let _: () = *s;
}
error[E0308]: mismatched types
 --> src/main.rs:5:17
  |
5 |     let _: () = Deref::deref(&s);
  |                 ^^^^^^^^^^^^^^^^ expected (), found &str
  |
  = note: expected type `()`
             found type `&str`

error[E0308]: mismatched types
 --> src/main.rs:6:17
  |
6 |     let _: () = *s;
  |                 ^^ expected (), found str
  |
  = note: expected type `()`
             found type `str`

的数据
deref的显式调用返回&str,但运算符*返回str。这更像是调用*Deref::deref(&s),忽略了隐含的无限递归(参见文档)。
Xirdus is correct in saying
如果deref返回一个值,它要么是无用的,因为它总是会被移出,要么具有与其他函数截然不同的语义
虽然“无用”有点强,但它仍然对实现Copy的类型有用。
另请参阅:

请注意,以上所有内容对于IndexIndexMut也有效。

unftdfkk

unftdfkk2#

编译器只知道如何解引用&-指针--但它也知道实现Deref trait的类型有一个deref()方法,可以用来获取对给定对象内部某些内容的适当引用。如果解引用一个对象,实际上你要做的是首先获取引用,然后才解引用它。
如果deref()返回一个值,它要么是无用的,因为它总是会被移出,要么具有与其他函数截然不同的语义,这并不好。

pes8fvy9

pes8fvy93#

去引用是两个部分的操作。一个是引用数据,另一个是访问引用后面的数据--这是在去引用操作符*的帮助下完成的。Deref trait只完成第一部分--引用然后给我们。然后去引用操作符*跟随引用(内存地址),并让我们访问该内存地址上的数据-这部分确实是编译器的内置功能。

fn main()
{
    let age: i32 = 90;
    let r_age = &age;       // first part: taking reference
    println!("{}", *r_age); // second part: accessing the reference
}

字符串
现在,如果我们没有引用,我们如何使用解引用操作符*访问数据?

fn main()
{
   let age: i32 = 90;
   //println!("{}", *age);   // will not work, we did not take reference of age.
   println!("{}", *(&age));
}


现在Deref trait可以为我们自己的类型实现了。

use std::ops::Deref; 

fn main()
{
   let d = OurOwnType(77);
   println!("{}", *d);
   println!("{}", *(d.deref()));
   println!("{}", *(Deref::deref(&d)));
   println!("{}", *(&d.0));
}

struct OurOwnType(i32);

impl std::ops::Deref for OurOwnType
{
    type Target = i32;
    fn deref(&self) -> &Self::Target
    {
        &self.0
    }
}


*d编译器的幕后,从Deref trait调用deref方法,如d.deref(),它给出了一个引用,然后在解引用操作符*的帮助下,解引用操作让我们访问数据。这里的Deref trait完成了给我们一个引用的第一部分。

相关问题