Почему возвращаемый тип Deref:: deref сам является ссылкой?

Я читал документы для Rust Deref trait:

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

Сигнатура типа для функции Deref кажется мне интуитивной; почему тип возврата является ссылкой? Если ссылки реализуют этот признак, чтобы их можно было разыменовать, какой эффект это имел бы вообще?

Единственное объяснение, которое я могу придумать, заключается в том, что ссылки не реализуют Deref, но считаются "примитивно разыменованными". Однако, как будет выглядеть полиморфная функция, которая будет работать для любого разыменованного типа, включая как Deref<T>, так и &T??

Ответ 1

что ссылки не реализуют Deref

Вы можете увидеть все типы, которые реализуют Deref, и &T находится в этом списке:

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

Deref вещь заключается в том, что синтаксический сахар применяется при использовании оператора * с чем-то, что реализует 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 правильно сказал

Если бы deref вернула значение, оно было бы бесполезным, потому что оно всегда перемещалось, или имело бы семантику, резко отличающуюся от любой другой функции.

Хотя "бесполезно" немного сильно; это все еще будет полезно для типов, которые реализуют Copy.

Смотрите также:

Обратите внимание, что все вышесказанное действительно верно и для Index и для IndexMut.

Ответ 2

Компилятор знает только, как разыгрывать & -pointers - но он также знает, что типы, реализующие trat Deref, имеют метод deref(), который может использоваться для получения соответствующей ссылки на что-то внутри данного объекта. Если вы разыменовываете объект, то, что вы на самом деле делаете, сначала получает ссылку и только затем разыгрывает ее.

Если deref() вернуло значение, это было бы бесполезно, потому что оно всегда выходило бы или имело бы семантику, которая сильно отличается от любой другой функции, которая не является приятной.