Понимание deref

Ниже приведен пример Rust Deref из http://doc.rust-lang.org/book/deref-coercions.html, за исключением того, что я добавил другое утверждение.

Мой вопрос: почему assert_eq с deref также равен 'a'? Оборотная сторона этого вопроса: зачем мне * после того, как я вручную вызвал deref?

use std::ops::Deref;

struct DerefExample<T> {
    value: T,
}

impl<T> Deref for DerefExample<T> {
    type Target = T;

    fn deref(&self) -> &T {
        &self.value
    }
}

fn main() {
    let x = DerefExample { value: 'a' };
    assert_eq!('a', *x.deref()); // this is true
    // assert_eq!('a', x.deref()); // this is a compile error
    assert_eq!('a', *x); // this is also true
    println!("ok");
}

Ответ 1

Во-первых, позвольте описать общие типы для вашего конкретного примера: 'a' is char, поэтому мы имеем (на самом деле не действительный синтаксис):

struct DerefExample<char> {
    value: char,
}

impl<char> Deref for DerefExample<char> {
    type Target = char;

    fn deref(&self) -> &char {
        &self.value
    }
}

Примечательно, что тип возврата deref является ссылкой на char. Таким образом, не удивительно, что при использовании только x.deref() результатом является &char, а не char (именно это компилятор скажет вам, если вы раскомментируете это утверждение). Помните, что в этот момент deref - это еще один нормальный метод — он просто неявно вызывается как часть специального синтаксиса, предоставляемого языком. *x, например, вызовет deref и разыграет результат, когда это применимо. x.char_method() и fn_taking_char(&x) также вызовет deref некоторое количество раз, а затем сделает что-то еще с результатом.

Почему deref возвращает ссылку для начала, вы спрашиваете? Разве это не кружок? Ну, нет, это не круговое: это уменьшает определенные в библиотеке интеллектуальные указатели на встроенный тип &T, который компилятор уже знает, как разыменовывать. И вернув ссылку вместо значения, вы избегаете копирования/перемещения (что может быть не всегда возможно!) И разрешите &*x (или &x при принуждении) ссылаться на фактический char, который DerefExample, а не временную копию.