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

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

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

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
}

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

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

Однако что-то подобное не компилируется:

fn main() {
    let mut c: i32 = 5;
    let mut next: i32 = 0;
    {
        let rc = &mut c;
        next = rc + 1;
    }
    println!("{}", next);
}
error[E0369]: binary operation `+` cannot be applied to type `&mut i32`
 --> src/main.rs:6:16
  |
6 |         next = rc + 1;
  |                ^^^^^^
  |
  = note: this is a reference to a type that `+` can be applied to; you need to dereference this variable once for this operation to work
  = note: an implementation of `std::ops::Add` might be missing for `&mut i32`

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

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

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


person John Smith Optional    schedule 31.03.2016    source источник


Ответы (3)


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

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

Разница между указателями C ++ и ссылками на 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 и попытаться использовать его в математическом выражении.

person oli_obk    schedule 31.03.2016
comment
Спасибо, теперь я понимаю. На самом деле, я думал, что это может быть что-то в этом роде, но я отбросил эту мысль, подумав, что было бы очень странно определять сумму целого числа с целым адресом таким образом. Возможно, в документе следует подчеркнуть, что ссылки на Rust следует рассматривать как адреса, а не путать со ссылками на C ++. Еще раз спасибо за подробное объяснение. - person John Smith Optional; 31.03.2016

Этот ответ предназначен для тех, кто ищет основы (например, из Google).

Из Ссылки и заимствования :

fn main() {
    let s1 = String::from("hello");

    let len = calculate_length(&s1);

    println!("The length of '{}' is {}.", s1, len);
}

fn calculate_length(s: &String) -> usize {
    s.len()
}

Эти амперсанды являются ссылками, и они позволяют вам ссылаться на некоторую ценность, не принимая на себя права собственности на нее [т.е. заимствования].

Противоположностью обращению с использованием & является разыменование, которое выполняется с помощью оператора разыменования *.

И простой пример:

let x = 5;
let y = &x; //set y to a reference to x

assert_eq!(5, x);
assert_eq!(5, *y); // dereference y

Если бы мы попытались вместо этого написать assert_eq!(5, y);, мы получили бы ошибку компиляции can't compare `{integer}` with `&{integer}`.

(Подробнее см. В главе Умные указатели < / а>.)

И из синтаксиса метода:

В Rust есть функция, называемая автоматическим обращением и разыменованием. Вызов методов - одно из немногих мест в Rust, которое имеет такое поведение.

Вот как это работает: когда вы вызываете метод с object.something(), Rust автоматически добавляет &, &mut или *, чтобы объект соответствовал сигнатуре метода. Другими словами, это то же самое:

p1.distance(&p2);
(&p1).distance(&p2);
person mb21    schedule 23.05.2018

Из документации для 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

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

person Jascha    schedule 31.03.2016