问题 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
}

似乎隐式解除引用(la C ++)对于不可变引用是正确的,但对于可变引用则不行。为什么是这样?


3007
2018-03-31 13:48


起源



答案:


在C ++中使用*来取消引用引用是不正确的。所以我想了解为什么这在Rust中是正确的。

C ++中的引用与Rust中的引用不同。 Rust的引用与C ++的指针更接近(在使用中,而不是在语义上)。关于内存表示,Rust的引用通常只是一个指针,而C ++的引用应该是同一个对象的替代名称(因此没有内存表示)。

C ++指针和Rust引用之间的区别在于Rust的引用永远不会 NULL从来没有未初始化,从不悬空。


Add trait是针对以下对和所有其他数字原语实现的(请参阅doc页面的底部):

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

这只是std-lib开发人员实现的一个方便的事情。编译器可以弄清楚a &mut i32 可以在任何地方使用 &i32 可以使用,但这对于泛型不起作用(但是?),因此std-lib开发人员还需要实现 Add 以下组合的特征(以及所有原语的特征):

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

正如你所看到的那样,可能会失控。我相信将来会消失。在那之前,请注意最终以a为结局 &mut i32 并尝试在数学表达式中使用它。


13
2018-03-31 14:02



谢谢,我现在明白了。实际上,我曾经认为它可能是那样的但是我已经驳回了这个想法,认为用这样的整数地址定义一个整数之和会很奇怪。也许应该在文档中强调Rust引用被认为是地址,而不是与C ++引用混淆。再次感谢您的详细解释。 - John Smith Optional


答案:


在C ++中使用*来取消引用引用是不正确的。所以我想了解为什么这在Rust中是正确的。

C ++中的引用与Rust中的引用不同。 Rust的引用与C ++的指针更接近(在使用中,而不是在语义上)。关于内存表示,Rust的引用通常只是一个指针,而C ++的引用应该是同一个对象的替代名称(因此没有内存表示)。

C ++指针和Rust引用之间的区别在于Rust的引用永远不会 NULL从来没有未初始化,从不悬空。


Add trait是针对以下对和所有其他数字原语实现的(请参阅doc页面的底部):

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

这只是std-lib开发人员实现的一个方便的事情。编译器可以弄清楚a &mut i32 可以在任何地方使用 &i32 可以使用,但这对于泛型不起作用(但是?),因此std-lib开发人员还需要实现 Add 以下组合的特征(以及所有原语的特征):

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

正如你所看到的那样,可能会失控。我相信将来会消失。在那之前,请注意最终以a为结局 &mut i32 并尝试在数学表达式中使用它。


13
2018-03-31 14:02



谢谢,我现在明白了。实际上,我曾经认为它可能是那样的但是我已经驳回了这个想法,认为用这样的整数地址定义一个整数之和会很奇怪。也许应该在文档中强调Rust引用被认为是地址,而不是与C ++引用混淆。再次感谢您的详细解释。 - John Smith Optional


来自文档 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

似乎数字的二元+运算符是针对操作数的共享(但不是可变的)引用和操作数的拥有版本的组合实现的。它与自动解除引用无关。


3
2018-03-31 14:05





这个答案适合那些寻找基础知识的人(例如来自谷歌)。

来自Rust的书 参考和借用

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);

0
2018-05-23 09:46