This is my minimal reproducible code (playground):
struct MyStruct {
my_string: String,
}
fn accepts_string(my_string: String) {
println!("my_string: {}", my_string)
}
fn accepts_struct_reference(my_struct: &MyStruct) {
accepts_string(my_struct.my_string);
}
fn main() {
let my_struct = MyStruct {
my_string: String::from("hi"),
};
accepts_struct_reference(&my_struct);
}
which produces:
error[E0507]: cannot move out of `my_struct.my_string` which is behind a shared reference
--> src/main.rs:10:20
|
10 | accepts_string(my_struct.my_string);
| ^^^^^^^^^^^^^^^^^^^ move occurs because `my_struct.my_string` has type `std::string::String`, which does not implement the `Copy` trait
I believe I understand why this error occurs: accepts_string tries to take the string away from the struct.
Why is that reference called a shared reference? Shared with whom? Does that adjective mean that there are non-shared references? If yes, what do they look like?
This is another way to distinguish mutable and immutable references.
In Rust, there is a clear distinction: the data can be either sharable (even if they're not shared currently), or directly mutable, but not both at once. This is achieved by having two types of references:
shared, or immutable ones, which can be copied but cannot be used to directly mutate data;
unique, or mutable ones, which cannot be copied (and this is UB, if you do this somehow), but can be used to mutate the data.
Note the "directly mutable" bit. Of course, it's sometimes possible to modify data through shared reference - if the data itself allows it; this is so-called internal mutability, based on multiple types like Cell or Mutex, which all internally use UnsafeCell - the only type explicitly allowed to be mutated behind shared reference.
More info might be found here: https://limpet.net/mbrubeck/2019/02/07/rust-a-unique-perspective.html
Related
I have been writing a program in Rust where I encountered an error relating to moving the data of a Shared Reference.
I made some research, but I was unable to find the cause of the error in the program I have written. Here is the simplified version of the program:
enum A {
Won = 1,
}
struct B {
result: A,
}
fn print_game(game: &B) {
println!("{}", game.result as u32);
}
fn main() {
let game: B = B { result: A::Won };
print_game(&game);
}
The above program when compiled, throws the below error:
error[E0507]: cannot move out of `game.result` which is behind a shared reference
--> src/main.rs:10:20
|
10 | println!("{}", game.result as u32);
| ^^^^^^^^^^^ move occurs because `game.result` has type `A`, which does not implement the `Copy` trait
From the error I can infer that the data in game.result is moved, but, I am not sure where it is moved and why it's been moved.
In rust, default behaviour for custom type is 'move' unless you implement Copy trait for the type.
If the value bound to a variable moves, the variable can not be used anymore. Developers new to Rust must get used to it.
Move is attempted in 'game.result as i32' portion of your code. Type casting is also counted as move for value of 'move' type.
I agree with the solution already mentioned above, just add the line
#[derive(Clone, Copy)]
Rust Playground
Traits like Deref, Into, From etc. could also be relevant, but depends...
By 'move' compiler means just transfer of ownership, not the change of data location in memory.
Suppose we have a variable v bound to a 'move' data. In a type casting like 'v as T' the ownership of data is detached from v and the bytes are reinterpreted as type T.
But why is this casting not allowed in your example?
Actual cause of error is that 'game' is a shared reference of type &B and you tried to use it to detach original ownership of data referred by it.
Shared reference can never be used to move referred data or part of it. <<
In this case I think your enum A just needs a #[derive(Clone, Copy)].
Rust Playground Example
Another option would be to refactor the program to only print what is actually needed:
…
fn main() {
let game: B = B { result: A::Won };
print_result(game.result);
}
Playground
I have the following code which you can also see in the playground:
struct Node<'a> {
parent: Option<&'a Node<'a>>,
name: &'a str,
}
fn looper(nodes: &mut Vec<Node>) {
for i in 0..nodes.len() {
let n = &nodes[i];
let next_node = Node {
parent: Some(n),
name: "please compile",
};
nodes.push(next_node);
}
}
It's supposed to take a Vec of Nodes and append Nodes to it that reference the Nodes already in the Vec. Unfortunately I get this error:
error[E0623]: lifetime mismatch
--> src/main.rs:13:20
|
6 | fn looper(nodes: &mut Vec<Node>) {
| -------------- these two types are declared with different lifetimes...
...
13 | nodes.push(next_node);
| ^^^^^^^^^ ...but data from `nodes` flows into `nodes` here
How can I make this compile? An explanation of how to think about lifetimes in this context would be useful. I understand this may be an issue due to variance but am not sure how to rephrase my problem to give me the functionality I would like.
A general rule: when some value's type, or trait it implements, has a lifetime parameter, that lifetime is always longer than the life of the value itself — the value has a guarantee that the references it contains won't go invalid before they are dropped.
In fact, in your example we can see that if this were not the case, the lifetime checking would be unsound; you're adding values to nodes, but nothing would prevent looper from instead removing values from nodes, which would then invalidate any parent references that referred to those values.
The only practical way to build a tree or linked list of references (without special-purpose unsafe code that has a particular plan to keep things sound) is to write a recursive function; each function call frame may refer to nodes constructed by its caller (and its caller's caller and so on). This is accepted by the borrow checker because each frame has its own new, shorter lifetime for the references (a reference can always be taken as having a shorter lifetime than it started with).
I understand you're not allowed to create two mutable references to an object at once in Rust. I don't entirely understand why the following code works:
fn main() {
let mut string = String::from("test");
let mutable_reference: &mut String = &mut string;
mutable_reference.push_str(" test");
// as I understand it, this creates a new mutable reference (2nd?)
test(&mut *mutable_reference);
println!("{}", mutable_reference);
}
fn test(s: &mut String) {
s.push_str(" test");
}
The rule
There shall only be one usable mutable reference to a particular value at any point in time.
This is NOT a spatial exclusion (there CAN be multiple references to the same piece) but a temporal exclusion.
The mechanism
In order to enforce this, &mut T is NOT Copy; therefore calling:
test(mutable_reference);
should move the reference into test.
Actually doing this would make it unusable later on and not be very ergonomic, so the Rust compiler inserts an automatic reborrowing, much like you did yourself:
test(&mut *mutable_reference);
You can force the move if you wanted to:
test({ let x = mutable_reference; x });
The effect
Re-borrowing is, in essence, just borrowing:
mutable_reference is borrowed for as long as the unnamed temporary mutable reference exists (or anything that borrows from it),
the unnamed temporary mutable reference is moved into test,
at the of expression, the unnamed temporary mutable reference is destroyed, and therefore the borrow of mutable_reference ends.
Is there more than one mutable pointer somewhere in memory referring to the same location? Yes.
Is there more than one mutable pointer in the code to the same location which is usable? No.
Re-borrowing a mutable pointer locks out the one you're re-borrowing from.
I analyzed the differences in MIR between test(mutable_reference) and test(&mut *mutable_reference). It appears that the latter only introduces an additional level of indirection:
When you are using an extra dereference in the function call, it doesn't create a mutable reference that holds; in other words, it doesn't cause any actual difference outside the function call.
I understand you're not allowed to create two mutable references to an object at once in Rust. I don't entirely understand why the following code works:
fn main() {
let mut string = String::from("test");
let mutable_reference: &mut String = &mut string;
mutable_reference.push_str(" test");
// as I understand it, this creates a new mutable reference (2nd?)
test(&mut *mutable_reference);
println!("{}", mutable_reference);
}
fn test(s: &mut String) {
s.push_str(" test");
}
The rule
There shall only be one usable mutable reference to a particular value at any point in time.
This is NOT a spatial exclusion (there CAN be multiple references to the same piece) but a temporal exclusion.
The mechanism
In order to enforce this, &mut T is NOT Copy; therefore calling:
test(mutable_reference);
should move the reference into test.
Actually doing this would make it unusable later on and not be very ergonomic, so the Rust compiler inserts an automatic reborrowing, much like you did yourself:
test(&mut *mutable_reference);
You can force the move if you wanted to:
test({ let x = mutable_reference; x });
The effect
Re-borrowing is, in essence, just borrowing:
mutable_reference is borrowed for as long as the unnamed temporary mutable reference exists (or anything that borrows from it),
the unnamed temporary mutable reference is moved into test,
at the of expression, the unnamed temporary mutable reference is destroyed, and therefore the borrow of mutable_reference ends.
Is there more than one mutable pointer somewhere in memory referring to the same location? Yes.
Is there more than one mutable pointer in the code to the same location which is usable? No.
Re-borrowing a mutable pointer locks out the one you're re-borrowing from.
I analyzed the differences in MIR between test(mutable_reference) and test(&mut *mutable_reference). It appears that the latter only introduces an additional level of indirection:
When you are using an extra dereference in the function call, it doesn't create a mutable reference that holds; in other words, it doesn't cause any actual difference outside the function call.
I'm reading through the Rust documentation on lifetimes. I tried something like:
struct S {
x: i8,
}
impl S {
fn fun(self) {}
fn print(&self) {
println!("{}", self.x);
}
}
fn main() {
let s = S { x: 1 };
s.fun();
s.print();
}
I get the following error:
error[E0382]: borrow of moved value: `s`
--> src/main.rs:16:5
|
15 | s.fun();
| - value moved here
16 | s.print();
| ^ value borrowed here after move
|
= note: move occurs because `s` has type `S`, which does not implement the `Copy` trait
This is because the fun(self) method takes ownership of the s instance. This is solved by changing to fun(&self).
I can't see why you would ever want to have a method on an object take control of itself. I can think of only one example, a destructor method, but if you wanted to do dispose of the object then it would be taken care of by the owner of the object anyway (i.e. scope of main in this example).
Why is it possible to write a method that takes ownership of the struct? Is there ever any circumstance where you would want this?
The idiomatic way to refer to a method that "takes control" of self in the Rust standard library documentation is to say that it "consumes" it. If you search for this, you should find some examples:
Option::unwrap_or_default
A lot in the Iterator trait.
As to why: you can try rewriting Iterator::map — you would end up having a lifetime parameter wandering around that would quickly become unmanageable. Why? Because the Map iterator is based upon the previous one, so the borrow checker will enforce that you can only use one of the two at the same time.
Conversion from type A to type B commonly involves functions taking self by value. See the implementors of Into and From traits for concrete examples.