【问题标题】:Meaning of the ampersand '&' and star '*' symbols in RustRust 中“&”和“*”符号的含义
【发布时间】:2016-07-20 00:30:52
【问题描述】:

尽管仔细阅读了文档,但我对 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
}

似乎隐式取消引用(a la C++)对于不可变引用是正确的,但对于可变引用则不然。这是为什么呢?

【问题讨论】:

    标签: reference rust dereference ampersand


    【解决方案1】:

    在 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 结尾并尝试在数学表达式中使用它是相当罕见的。

    【讨论】:

    • 谢谢,我现在明白了。实际上,我曾认为它可能是这样的,但我打消了这个想法,认为用这样的整数地址定义整数之和会很奇怪。也许应该在文档中强调 Rust 引用被认为是地址,而不是与 C++ 引用混淆。再次感谢您的详细解释。
    【解决方案2】:

    此答案适用于那些寻找基础知识(例如来自 Google)的人。

    来自 Rust 书的References and Borrowing

    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}`

    (您可以在Smart Pointers chapter阅读更多内容。)

    来自Method Syntax

    Rust 有一个称为自动引用和解除引用的功能。调用方法是 Rust 中少数具有这种行为的地方之一。

    它是这样工作的:当你用 object.something() 调用方法时,Rust 会自动添加 &&mut*,因此对象与方法的签名相匹配。换句话说,以下是相同的:

    p1.distance(&p2);
    (&p1).distance(&p2);
    

    【讨论】:

      【解决方案3】:

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

      似乎数字的二进制 + 运算符是为操作数的共享(但不可变)引用和操作数的拥有版本的组合实现的。它与自动取消引用无关。

      【讨论】:

        猜你喜欢
        • 2013-07-01
        • 2021-09-15
        • 2013-07-18
        • 2016-10-25
        • 1970-01-01
        • 2014-09-14
        • 2012-06-12
        • 2017-01-02
        • 1970-01-01
        相关资源
        最近更新 更多