I am trying to understand how lifetimes in Rust affect Structs. Attached is a minimal example that confuses me.
I would like to give a struct a reference to an object and then later it is necessary to change this object. Since it is not possible to change the object while it is borrowed, I thought that I have to remove the reference while changing it. So my idea was to define the variable as an option and remove the reference during the modification of the object by setting it to None
. For the example without the use of a Struct this seems to work.
However, if I now put this reference into a Struct it does not work. It seems to me that the borrow checker overlooks the fact that the variable string
is no longer borrowed. Is there a way to still achieve the desired behaviour with a Struct?
This works (link to playground):
fn main() {
let mut string = "mutable string".to_string();
let mut ref_to_string: Option<&str> = Some(&string);
ref_to_string = None;
string = "mutated string".to_string();
ref_to_string = Some(&string);
}
However, this doesn't work link to playground:
struct Foo<'a> {
pub ref_data: Option<&'a str>,
}
fn main() {
let mut string = "mutable string".to_string();
let mut foo = Foo{ref_data: Some(&string)};
foo.ref_data = None;
string = "mutated string".to_string();
foo.ref_data = Some(&string);
}
Error message:
error[E0506]: cannot assign to `string` because it is borrowed
--> src/main.rs:11:5
|
9 | let mut foo = Foo{ref_data: Some(&string)};
| ------- borrow of `string` occurs here
10 | foo.ref_data = None;
11 | string = "mutated string".to_string();
| ^^^^^^ assignment to borrowed `string` occurs here
12 | foo.ref_data = Some(&string);
| ---------------------------- borrow later used here
For more information about this error, try `rustc --explain E0506`.
In general, a struct should only contain a public and/or mutable field of a reference type if one of the following conditions applies: All instances of that type may be regarded as inherently immutable (as is the case of `string`)
5, the simple types provided by C#, such as int , double , and bool , are, in fact, all struct types.
3 answers. It is not possible to set a struct with NULL as it is declared. Fila f = NUll; error: invalid initializer. So cast% from% to NULL , which is not even a type in this code, or assign Fila to a primitive type variable is "wrong".
The problem is that you are referencing the string
variable twice. When borrowing the first one is binding the &str
lifetime to be alive at least as foo
. There should be no problem to reference another string that lives in "another" variable:
fn main() {
let string = "mutable string".to_string();
let mut foo = Foo {
ref_data: Some(&string),
};
foo.ref_data = None;
let string = "mutated string".to_string();
foo.ref_data = Some(&string);
}
Playground
Notice the use of another let
binding instead of mutating the variable.
The secret lies in the definition of the Foo
struct:
struct Foo<'a> {
pub ref_data: Option<&'a str>,
}
And more specifically in the 'a
lifetime specifier. By specifying that Foo
lives for 'a
and that ref_data
has the same lifetime (&'a str
), you say that your &str
will live as long as Foo
. So as long as Foo
is alive, compiler cannot drop the reference to your string
.
Unfortunately, there is no way to specify that the struct has lifetime of 'a
only while the field is Some
.
To mutate an object refenced by a structure, you can:
RefCell
).Which approach is better depends on your specific use case.
The problem is that the whole Foo
is generic over a lifetime. That way, when you assigned foo = Foo {ref_data: Some(&string)}
, the type of foo
is Foo<'lifetime_of_string>
. You can set foo.ref_data
to None
all you want, but as far as borrow checking is concerned, there is still a live object whose lifetime overlaps that of string
. For this reason, string
is borrowed as long as foo
lives. The only way to do it is to drop the whole foo
:
let mut string = "mutable string".to_string();
let mut foo = Foo{ref_data: Some(&string)};
drop(foo);
foo = Foo { ref_data: None };
string = "mutated string".to_string();
foo.ref_data = Some(&string);
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With