Значение символа амперсанда '&' и звезды '*' в Rust

Несмотря на тщательное чтение документации, я довольно запутался в значении символа & и * в Rust, и в более общем плане о том, что именно является ссылкой Rust.

В этом примере это похоже на ссылку С++ (то есть адрес, который автоматически разыменовывается при использовании):

fn main() {
    let c: i32 = 5;
    let rc = &c;
    let next = rc + 1;
    println!("{}", next); // 6
}

Однако следующий код работает точно так же:

fn main() {
    let c: i32 = 5;
    let rc = &c;
    let next = *rc + 1;
    println!("{}", next); // 6
}

Использование * для разыменования ссылки не будет корректным в С++. Поэтому я хотел бы понять, почему это правильно в Rust.

До сих пор я понимаю, что вставка * перед ссылкой Rust вызывает его разбор, но * неявно вставлена, так что вам не нужно ее добавлять (в то время как на С++ она неявно вставлена и если вы вставляете его, вы получаете ошибку компиляции).

Однако, что-то вроде этого не компилируется:

fn main() {
    let mut c: i32 = 5;
    let mut next: i32 = 0;
    {
        let rc = &mut c;
        next = rc + 1;
    }
    println!("{}", next);
}
<anon>:6:16: 6:18 error: binary operation `+` cannot be applied to type `&mut i32` [E0369]
<anon>:6         next = rc + 1;
                        ^~
<anon>:6:16: 6:18 help: see the detailed explanation for E0369
<anon>:6:16: 6:18 note: an implementation of `std::ops::Add` might be missing for `&mut i32`
<anon>:6         next = rc + 1;

Но это работает:

fn main() {
    let mut c: i32 = 5;
    let mut next: i32 = 0;
    {
        let rc = &mut c;
        next = *rc + 1;
    }
    println!("{}", next);  // 6
}

Кажется, что неявное разыменование (a la С++) верно для неизменяемых ссылок, но не для изменяемых ссылок. Почему это?

Ответ 1

Использование * для разыменования ссылки не будет корректным в С++. Поэтому я хотел бы понять, почему это правильно в Rust.

Ссылка в С++ не совпадает с ссылкой в ​​Rust. Ссылки на ржавчину гораздо ближе (в использовании, а не в семантике) к указателям на С++. Что касается представления памяти, ссылки Rust часто являются всего лишь одним указателем, в то время как ссылки на С++ должны быть альтернативными именами одного и того же объекта (и, следовательно, не имеют представления памяти).

Разница между указателями С++ и ссылками Rust заключается в том, что ссылки Rust никогда не NULL, никогда неинициализированные и никогда не оборванные.


Функция Add реализована (см. нижнюю часть страницы документа) для следующих пар и всех других числовых примитивов:

  • &i32 + i32
  • i32 + &i32
  • &i32 + &i32

Это просто удобная вещь, реализованная разработчиками std-lib. Компилятор может понять, что &mut i32 можно использовать везде, где можно использовать &i32, но это не работает (пока?) Для дженериков, поэтому разработчикам std-lib также нужно будет реализовать Add черты для следующих комбинаций (и для всех примитивов):

  • &mut i32 + i32
  • i32 + &mut i32
  • &mut i32 + &mut i32
  • &mut i32 + &i32
  • &i32 + &mut i32

Как вы можете видеть, это может вырваться из-под контроля. Я уверен, что это исчезнет в будущем. До тех пор обратите внимание, что довольно редко заканчивается &mut i32 и пытается использовать его в математическом выражении.

Ответ 2

Из документов для std::ops::Add:

impl<'a, 'b> Add<&'a i32> for &'b i32
impl<'a> Add<&'a i32> for i32
impl<'a> Add<i32> for &'a i32
impl Add<i32> for i32

Кажется, что двоичный + оператор для чисел реализуется для комбинаций общих (но не изменяемых) ссылок операндов и принадлежащих ему версий операндов. Это не имеет никакого отношения к автоматическому разыменованию.