Значение символа амперсанда '&' и звезды '*' в 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
Кажется, что двоичный + оператор для чисел реализуется для комбинаций общих (но не изменяемых) ссылок операндов и принадлежащих ему версий операндов. Это не имеет никакого отношения к автоматическому разыменованию.