简体   繁体   English

如何在 Rust 中拥有多个可以访问其值的共享引用?

[英]How does having multiple shared references which value can be accessed just works in Rust?

I'm new to Rust and already have read "the book" but I'm trying to understand the inners of references in the language.我是 Rust 的新手,并且已经阅读过“这本书”,但我正在尝试了解该语言中参考文献的内在内容。

As far as I know a reference is a type of pointer that takes you to some value in memory when dereferenced.据我所知,引用是一种指针,当取消引用时,它会将您带到内存中的某个值。

let x = 5
let y = &x

In this example y is a pointer to the memory address of x and so the type and value of y is not equal to the type and value of x .在此示例中, y是指向x的内存地址的指针,因此y的类型和值不等于x的类型和值。

assert_eq!(y, x)
// fails to compile as assert_eq! has no implementation for `&{integer} == {integer}`
  1. Then a reference is not the same as the value it references to.那么引用与它所引用的值不同。

But if I dereference the y by using the * operator I now do get the value it referenced and so the following code compiles.但是,如果我使用*运算符取消对y的引用,我现在确实得到了它引用的值,因此下面的代码可以编译。

assert_eq!(*y, x)
  1. To access the value the reference points to, dereferencing is needed;要访问引用指向的值,需要取消引用; but dereferencing implies moving the ownership of the referenced value to the new variable.但是取消引用意味着将引用值的所有权转移到新变量。
let x = Point {x:1, y:2};
let y = &x;
let z = *y;
// fails to compile as move occurs because `*y` has type `Point`, which does not implement the `Copy` trait
  1. By implementing the Copy trait for the type ( Point in this case) the problem can be solved by letting Rust create a copy of the value and move the ownership of the copied value to the new variable.通过为类型(在本例中为Point )实现Copy trait,可以通过让 Rust 创建值的副本并将复制值的所有权转移到新变量来解决问题。

The final question is, how can Rust access a value of a type that does not implement the Copy or Clone traits and is behind a reference without having the dereference ( * ) function take ownership of the value, thus making other shared references to the original value invalid?最后一个问题是,Rust 如何访问一个没有实现CopyClone特征并且在引用后面的类型的值,而不让解引用 ( * ) 函数获得该值的所有权,从而使其他共享引用对原始值无效?

Eg (works just fine)例如(工作得很好)

let x = Point {x:1, y:2};
let y = &x;
let z = &x;

fn print_point(a: &Point){
    println!("{a:#?}")
}

println!("Printing y");
print_point(y);
    
println!("Printing x");
println!("{x:#?}");

println!("Printing z");
print_point(z);

( Playground ) 游乐场

The ownership semantics of Rust are more complex than simply giving ownership or denying access. Rust 的所有权语义比简单地授予所有权或拒绝访问更复杂。

First, there is a little background to be explained about pointers in Rust .首先,关于Rust中的指针有一点背景需要解释。 A pointer is just an integer that indicates an index of the memory.指针只是一个整数,表示内存的索引。 It's usually beginner-friendly to think of it as an arrow pointing to a location in memory.将其视为指向内存中某个位置的箭头通常对初学者很友好。 An arrow is not what it points to, but it's easy to get the value pointed by an arrow given the arrow (just look where it points to).箭头不是它所指向的,但很容易得到给定箭头​​的箭头所指向的值(只需查看它指向的位置)。

All pointer-like in Rust are either just that arrow, or that arrow a little more information about that value (so that you don't have to read the value to know that information). Rust 中所有类似指针的东西要么只是那个箭头,要么就是那个箭头关于那个值的更多信息(这样你就不必读取这个值来知道那个信息)。 By pointer-like I mean anything that behaves like a pointer, that is, can be dereferenced (to keep it simple).我所说的指针式是指任何表现得像指针的东西,也就是说,可以取消引用(为了保持简单)。

For example, the borrow of x ( &x ) is not exactly a pointer ("actual" pointers in Rust are used less often than borrows and would be either *mut x or *const x ), it's a pointer-like.例如, x ( &x ) 的借用并不完全是一个指针(Rust 中的“实际”指针比借用更少使用,并且可以是*mut x*const x ),它是一个类似指针的指针。 This pointer-like is just an integer: once your program is compilated, you couldn't tell the difference between a borrow and a pointer.这个类指针只是一个整数:一旦你的程序被编译,你就无法区分借用和指针之间的区别。 However, contrary to "just a number", a borrow holds some additional constraints.然而,与“只是一个数字”相反,借位有一些额外的限制。 For instance, it's never a null-pointer (that is, a pointer that points to the very-first position of the memory; by convention — and for technical reasons concerning the allocator — this area is never allocated, that is, this pointer is always invalid ).例如,它永远不是空指针(即指向内存第一个位置的指针;按照惯例——出于与分配器有关的技术原因——这个区域永远不会被分配,也就是说,这个指针是总是无效的)。 In fact, more generally, a borrow should never be invalid : it should always be safe to dereference it (NB. safe doesn't mean legal , Rust could prevent you from dereferencing it, as in the example you posted), in the sense that the value that it points to always "makes sense".实际上,更一般地说,借用永远不应该是无效的:从某种意义上说,取消引用它应该始终是安全的(注意。安全并不意味着合法,Rust 可能会阻止您取消引用它,就像您发布的示例一样)它指向的价值总是“有意义的”。 This is still not the strongest guarantee provided by a borrow, which is that, for as long as someone holds the borrow (that is, for as long as someone could use it), no one is going to modify the value it points to.这仍然不是借用提供的最强保证,即只要有人持有借用(即只要有人可以使用它),就没有人会修改它所指向的值。 This is not something you have to be careful about: Rust will prevent you from writing code that could break this.这不是你必须要小心的事情:Rust 会阻止你编写可能破坏这一点的代码。

Similarly, a mutable borrow ( &mut x ) is also a pointer-like that is "just an integer", but has different constraints.类似地,可变借用&mut x )也是类似指针的“只是一个整数”,但具有不同的约束。 A mutable borrow (also called "exclusive borrow"), besides always being valid, ensures that, as long as one person is holding it, no one else can access the value it points to, either to modify it or just to write it.可变借用(也称为“独占借用”)除了始终有效之外,还确保只要一个人持有它,其他人就无法访问它指向的值,无论是修改它还是仅仅写入它。 This is to prevent data races, because the one that hold the exclusive borrow can modify the value, even though they don't own the value.这是为了防止数据竞争,因为持有独占借用的人可以修改该值,即使他们不拥有该值。

These are definitively the most common pointer-like used in Rust (they're everywhere), and could be seen as the read-only but multi-access version of a pointer, or the read-and-write but single-access version of a pointer.这些绝对是 Rust 中最常见的类指针(它们无处不在),并且可以被视为指针的只读但多访问版本,或读写但单访问版本一个指针。


Having understood that, it's easier to understand Rust's logic.明白了这一点,就更容易理解 Rust 的逻辑了。 If I have y = &x , I am allowed to read the value x , but I can't "take" it (that is, take its ownership): I can't even write to it (but I should be able to if I owned it)!如果我有y = &x ,我可以读取值x ,但我不能“获取”它(即,获取它的所有权):我什至不能写入它(但我应该能够如果我拥有它)! Note that even if I have an exclusive borrow, I couldn't take the ownership of x , but I could "swap" it: take its ownership in exchange for the ownership of a variable I owned (or create an owned variable for the occasion).请注意,即使我有一个独占借用,我也不能取得x的所有权,但我可以“交换”它:取得它的所有权以换取我拥有的变量的所有权(或为此场合创建一个拥有的变量)。 For this reason, if you write let z = *y , you are taking the ownership of x so Rust complains.出于这个原因,如果你写let z = *y ,你就获得了x的所有权,所以 Rust 会抱怨。 But note that this is due to the binding, not the the dereferencing of y .但请注意,这是由于绑定,而不是y的取消引用 To prove it, compare it with the following ( String is not Copy ):为了证明这一点,将其与以下内容进行比较( String is not Copy ):

let a = String::new();
let b = &a;
let c = &a;
assert_eq(*b, *c);

playground 操场

Here, I dereference the borrow to a non- Copy value, but it's clear that I am not violating the borrow contract (to compare equality, I just need to read the value).在这里,我将借用取消引用到非Copy值,但很明显我没有违反借用合同(为了比较相等性,我只需要读取值)。

Furthermore, in general, if I have a borrow of a struct , I can obtain borrows of the its fields.此外,一般来说,如果我借用了一个struct ,我可以获得它的字段的借用。


Incidentally, note that String is a pointer-like too!顺便说一句,请注意String也是类似指针的! It's not "just a number" (meaning it carries more information than just being an arrow).它不是“只是一个数字”(意味着它携带的信息不仅仅是一个箭头)。 These pointer-like that are more than "just a number" are called fat pointers.这些不仅仅是“数字”的类指针称为胖指针。 String is a fat pointer to an owned str , that is, String is a kind of pointer that also ensure that whomever owns it also owns the pointed value, which is why String is not Copy (otherwise, the pointed value could be owned by multiple parties). String是指向拥有的str的胖指针,也就是说, String是一种指针,它还确保拥有它的人也拥有指向的值,这就是String不是Copy的原因(否则,指向的值可能被多个拥有派对)。

To access the value the reference points to, dereferencing is needed;要访问引用指向的值,需要取消引用; but dereferencing implies moving the ownership of the referenced value to the new variable.但是取消引用意味着将引用值的所有权转移到新变量。

Dereferencing does not imply moving.取消引用并不意味着移动。 The motivating example presented let z = *y simply dereferences y and then moves/copies the value to z .给出的激励示例let z = *y简单地取消引用y ,然后将值移动/复制到z Yet if you do not assign the value to a new variable there is no new variable and therfore no transfer of ownership.但是,如果您不将值分配给新变量,则没有新变量,因此不会转移所有权。

This might help building an intuition:这可能有助于建立直觉:

You make the (correct) case that a reference is a different thing from the object it points to.您提出了(正确的)情况,即引用与其指向的对象不同。 However implying that you would need to assign it after dereferencing in order to do anything with it (wrong).但是暗示您需要在取消引用后分配它才能对其进行任何操作(错误)。 Most of the time you are fine working directly on the members, which may be copyable.大多数情况下,您可以直接在成员上工作,这可能是可复制的。 If not maybe the members of the members are.如果不是,那么成员的成员可能是。 In the end it is likely to all just to boil down to bytes.最后很可能只是归结为字节。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM