Borrow Checker Not Releasing Immutable Borrow - rust

I'm having a problem in the code below where the borrow checker complains about an immutable borrow in the call to self.entries.push() despite placing all the temporary code inside its own scope.
I have checked other posts with similar problems, but I can't figure out how to adapt the code to my own situation -- I'm still pretty new to Rust. Suggestions?
impl Entry {
pub fn chain(&self, spair: &SigningPair, expires: u16)
-> Result<(Entry, HashMap<&str,CryptoString>), MensagoError> {
// New Entry and HashMap allocated in here to be returned
}
}
pub struct Keycard {
_type: EntryType,
pub entries: Vec<Entry>,
}
impl Keycard {
pub fn get_current(&self) -> Option<&Entry> {
// ...
}
pub fn chain(&mut self, spair: &SigningPair, expires: u16)
-> Result<HashMap<&str,CryptoString>, MensagoError> {
let (newentry, keys) = {
let entry = match self.get_current() {
Some(v) => v,
None => { return Err(MensagoError::ErrEmptyData) }
};
match entry.get_field("Type")?.as_str() {
"Organization" | "User" => (),
_ => { return Err(MensagoError::ErrInvalidKeycard) }
}
entry.chain(spair, expires)?
};
self.entries.push(newentry);
Ok(keys)
}
}

I'm fairly certain that the error is because the returnvalue of chain() is incorrect:
HashMap<&str,CryptoString> should be HashMap<&'static str,CryptoString> or HashMap<String,CryptoString>
The explanation is a little bit longer, though.
Rust borrow checker demands that a value can be:
borrowed immutably by many borrowers or
borrowed mutably by exactly one borrower
no other borrows can exist (mutable or immutable) when a value gets modified
You try to modify the value self.entry at self.entries.push(newentry). Therefore, there must not be any borrows that reference self.entry.
Sadly, a borrow chain exists that borrows self, which indirectly also borrows self.entry at that point in time:
the type of newentry contains a reference, &str. newentry got created in .chain(), where &str has no lifetime annotations and therefore has the same lifetime as entry.
entry is created by self.get_current(), where again, &Entry is a reference with no explicit lifetime annotation and therefore has the same lifetime as self.
Which means through the chain newentry -> entry -> self, the self object is still borrowed while you try to call self.entries.push(). This is exactly what the error message is trying to tell you.
There are several solutions to those problems usually:
Introduce Rc instead of references
.clone() somewhere in between to break the reference chain
make sure that the reference chain actually makes sense in the first place, and if not, introduce lifetimes appropriately
In your case I think it's solution #3, as there is no reason why newentry should borrow entry. The type &str is most likely incorrect and should be &'static str. In my experience, using &str as a key for HashMap doesn't make much sense, it should be either &'static str (= global constant string like "hello") or the owned, mutable version String. Using a temporary reference as a key is quite strange and therefore most likely a beginner error.
The fixed version is:
use std::collections::HashMap;
pub struct Entry;
pub struct SigningPair;
pub struct CryptoString;
pub enum MensagoError {
ErrEmptyData,
ErrInvalidKeycard,
}
pub struct EntryType;
impl Entry {
pub fn chain(
&self,
_: &SigningPair,
_: u16,
) -> Result<(Entry, HashMap<&'static str, CryptoString>), MensagoError> {
todo!()
}
fn get_field(&self, _: &str) -> Result<String, MensagoError> {
todo!()
}
}
pub struct Keycard {
_type: EntryType,
pub entries: Vec<Entry>,
}
impl Keycard {
pub fn get_current(&self) -> Option<&Entry> {
todo!()
}
pub fn chain(
&mut self,
spair: &SigningPair,
expires: u16,
) -> Result<HashMap<&str, CryptoString>, MensagoError> {
let (newentry, keys) = {
let entry = match self.get_current() {
Some(v) => v,
None => return Err(MensagoError::ErrEmptyData),
};
match entry.get_field("Type")?.as_str() {
"Organization" | "User" => (),
_ => return Err(MensagoError::ErrInvalidKeycard),
}
entry.chain(spair, expires)?
};
self.entries.push(newentry);
Ok(keys)
}
}

Related

How can I create values that outlive 'static lifetime?

I want to implement Into trait for my struct.
'static is hinted by the compiler, I do not know why I have to add that.
I hope:
invoking into() consumes the RequestBody object. (which already did)
the references to str inside returned HashMap live until the end of scope where into() is invoked.
Below is the error message and code, how can I make it compile as I wish?
error: lifetime may not live long enough
--> src/portal_request.rs:35:9
|
26 | fn into(self) -> HashMap<&'static str, &'static str> {
| ---- has type `RequestBody<'1>`
...
35 | list.into_iter().collect()
| ^^^^^^^^^^^^^^^^^^^^^^^^^^ returning this value requires that `'1` must outlive `'static`
pub enum BodyEntry<'a> {
MenuId(&'a str),
//...
}
pub struct RequestBody<'a> {
pub entries: Vec<BodyEntry<'a>>,
}
use BodyEntry::*;
impl Into<HashMap<&str, &str>> for RequestBody<'_> {
fn into(self) -> HashMap<&'static str, &'static str> {
let mut list = Vec::new();
for entry in self.entries {
let entry_literal = match entry {
MenuId(val) => ("_menuId", val),
//...
_ => ("else", "else"),
};
list.push(entry_literal);
}
list.into_iter().collect()
}
}
You need to declare the lifetimes like so:
impl<'a> Into<HashMap<&'static str, &'a str>> for RequestBody<'a> {
fn into(self) -> HashMap<&'static str, &'a str> {
Or better:
impl<'keys, 'a> Into<HashMap<&'keys str, &'a str>> for RequestBody<'a> {
fn into(self) -> HashMap<&'keys str, &'a str> {
You can't. You can't create new 'static string at runtime. Your keys are fine, because they're all determined at compile-time (at least in this minimal example; if your real example needs dynamically-generated ones then the same caveat applies). But you want to consume the BodyEntry and create a HashMap which owns the strings as values. An owned string is not a &str. An owned string is a String.
Consider having your Into implementation return HashMap<&str, String> (again, you might have to change the key type to String as well, if your real use case demands it). Then, inside your match, consider
let entry_literal = match entry {
MenuId(val) => ("_menuId", String::from(val)),
//...
_ => ("else", String::from("else")),
};
String::from copies the underlying &str string slice into a new owned String.

Why RefCell's `into_inner` requires a move?

I have a situation where I have to move a struct from one object to another through a &mut self. Take a look:
pub struct MyStruct {
//no copy trait
device: Device
}
impl MyStruct {
pub fn finalize(&mut self) {
//error: cannot move since I borrowed
let interface = InterfaceBuilder::new(self.device)
}
}
First of all, why I cannot move something out of a borrowed mutable reference? Borrowed mutables are exclusive, there's no chance another code is looking into it.
Well, to address this problem I changed to
pub struct MyStruct {
//no copy trait
device: RefCell<Device>
}
impl MyStruct {
pub fn finalize(&mut self) {
//error on `self.device`: cannot move out of `self.device` which is behind a mutable reference
let interface = InterfaceBuilder::new(self.device.into_inner())
}
}
I know why the error occurs:
pub fn into_inner(self) -> T
calling into_inner makes self.device move. Why RefCell simply does not have an implementation pub fn into_inner(&mut self) -> T? I don't see a problem.
You cannot move out of a mutable reference because that would leave the original object incomplete.
Consider this code:
struct MyStruct {
s: String
}
fn finalize(f: &mut MyStruct) {
let _x = f.s; //error E0507!
}
fn main() {
let mut my = MyStruct {
s: "hi".into()
};
finalize(&mut my);
println!("{}", my.s); //what should this do?
}
Then, RefCell::into_inner(&mut self) -> T has the same problem. You could call it twice in a row and you would get two T values where before there was only one. And that, for a non Copy type is impossible.
If you want this function to consume the inner value, probably it should consume the outer value too:
fn finalize(f: MyStruct) {
let _x = f.s;
}
If you really want to move a value out of a mutable reference, you must leave something valid in its place. The easiest way is to declare an Option and use take() to steal and replace it with a None:
struct MyStruct {
s: Option<String>
}
fn finalize(f: &mut MyStruct) {
let _x = f.s.take();
}
Naturally, Option::take returns an Option so that if you call it twice, the second time you get None. If you are positive you have a value you can do take().uwnrap().
Alternatively, if your field type is Default you can use std::mem::take that replaces it with a default-created value:
struct MyStruct {
s: Vec<i32>
}
fn finalize(f: &mut MyStruct) {
let _x = std::mem::take(&mut f.s);
}
PS #1: there is Cell::take(&self) -> T, but only if T implements Default. It works just like std::mem::take but with a non-mutable reference.
PS #2: there is also unsafe fn ManuallyDrop::take(slot: &mut ManuallyDrop<T>) -> T, that is intented to be used in advanced drop implementations. But it is unsafe so it should never be your first option: if you call it twice you will get undefined behavior.

HashMap in a Struct error: cannot borrow data in a "&" reference as mutable

I have the following code:
struct Node{
node_map: HashMap<char, Node>,
value: Option<i32>,
}
struct Trie {
root: Node,
}
impl Trie {
fn new() -> Trie {
Trie {
root: Node{
node_map: HashMap::new(),
value: None,
},
}
}
fn find(&self, key: &String) -> Option<&Node> {
// Returning some Option<&Node>
}
fn delete(&mut self, key: &String) -> Option<i32> {
// extract code snippet
let mut search_node = self.find(key);
if search_node.is_some() {
search_node.unwrap().node_map.remove(& 'x');
}
None
}
}
Rust complains the error under search_node.unwrap().chs part: cannot borrow data in a "&" reference as mutable
So I understand that the find function returns Option<&Node> so when unwrapping at the above line, I get the reference to Node.
Attempts:
I tried to dereference the node by: *search_node.unwrap().node_map.remove(& 'x'); or *(search_node.unwrap()).node_map.remove(& 'x'); but it still throws error.
I followed another answer here and tried to make node_map mutable like:
struct Node<'a> {
node_map: &'a mut HashMap<char, Node<'a>>,
value: Option<i32>,
}
But then I got complain about lacking lifetime several places. One particular place I dont know how to add is in the new function.
Please let me know how to solve the original issue or how to add the appropriate lifetime.
The problem is that find returns an (optional) immutable reference, but then you try to mutate it later. For that reason, you'll probably want to add a method find_mut with signature
fn find_mut(&mut self, key: &str) -> Option<&mut Node>
(I changed the key argument to &str because it's discouraged to take &String as an argument)
Another stylistic thing: you should use if let instead of checking that search_node is some and then unwrapping.
if let Some(search_node) = self.find_mut(key) {
search_node.node_map.remove(&'x');
}

Convert Arc<RwLock> to &mut

I am trying to have a value in a trait that can be mutated by means of a reference. The problem is that the String values are very large and may be accessed by many threads, so my solution looks something like this:
trait MyTrait {
fn name<'a>(&'a mut self) -> &'a mut String;
}
struct SimpleImpl {
name: String
}
impl MyTrait for SimpleImpl {
fn name<'a>(&'a mut self) -> &'a mut String {
&mut self.name
}
}
use std::sync::{Arc,RwLock};
struct ParallelImpl {
name: Arc<RwLock<String>>
}
impl MyTrait for ParallelImpl {
fn name<'a>(&'a mut self) -> &'a mut String {
self.name.get_mut().unwrap()
}
}
fn main() {
let mut a = SimpleImpl { name: String::from("simple") };
let mut b = ParallelImpl { name: Arc::new(RwLock::new(String::from("parallel"))) };
a.name().as_mut_str();
b.name().as_mut_str();
}
This fails to compile with
main2.rs:23:9: 23:18 error: cannot borrow immutable borrowed content as mutable
main2.rs:23 self.name.get_mut().unwrap()
Why can't I call get_mut() to unwrap both the Arc and the RwLock?
Have a better look at the interface of RwLock.
get_mut returns a LockResult<&mut T> which is a guard object. The destruction of this guard automatically unlocks the lock.
In order for things to be safe, the &mut T that you get by calling unwrap() on the guard is borrowing from the guard, that is, the lifetime of the result of unwrap() is limited by that of the guard (since after the guard is destroyed, the lock is unlocked).
And here, you are creating a temporary guard and throwing it away immediately, so the lifetime of the reference cannot exceed that of the function...
Congratz to Rust! Yet another data race prevented at compile-time :)

Lifetime error for returned value of a function

This is a simplified version of a piece of code I am trying to implement:
struct FirstStruct
{
a: i8,
}
impl FirstStruct
{
fn get(&self) -> Option<&str>
{
Some("aaa")
}
}
pub struct SecondStruct<'a>
{
pub name: Option<&'a str>,
}
impl<'a> SecondStruct<'a>
{
fn extract_string(obj: &/*'a*/ FirstStruct) -> Option<&'a str>
{
obj.get() //this is where the error happen
}
pub fn from_string() -> SecondStruct<'a>
{
let obj = FirstStruct{a: 1};
SecondStruct{
name: SecondStruct::extract_string(&obj),
}
}
}
fn main()
{
let g_def_res = SecondStruct::from_string();
}
This code throws the following error :
test2.rs:23:13: 23:18 error: cannot infer an appropriate lifetime for autoref due to conflicting requirements
test2.rs:23 obj.get() //this is where the error happen
^~~~~
test2.rs:21:5: 24:6 help: consider using an explicit lifetime parameter as shown: fn extract_string(obj: &'a FirstStruct) -> Option<&'a str>
test2.rs:21 fn extract_string(obj: &FirstStruct) -> Option<&'a str>
test2.rs:22 {
test2.rs:23 obj.get() //this is where the error happen
test2.rs:24 }
error: aborting due to previous error
Applying the proposed solution throw this error :
test2.rs:30:55: 30:58 error: `obj` does not live long enough
test2.rs:30 name: SecondStruct::extract_string(&obj),
^~~
test2.rs:27:5: 32:6 note: reference must be valid for the lifetime 'a as defined on the block at 27:4...
test2.rs:27 {
test2.rs:28 let obj = FirstStruct{a: 1};
test2.rs:29 SecondStruct{
test2.rs:30 name: SecondStruct::extract_string(&obj),
test2.rs:31 }
test2.rs:32 }
test2.rs:28:37: 32:6 note: ...but borrowed value is only valid for the block suffix following statement 0 at 28:36
test2.rs:28 let obj = FirstStruct{a: 1};
test2.rs:29 SecondStruct{
test2.rs:30 name: SecondStruct::extract_string(&obj),
test2.rs:31 }
test2.rs:32 }
error: aborting due to previous error
To summarise:
How to say that the return value of FirstStruct::get must have the lifetime of either [the return value of SecondStruct::from_str | the struct lifetime 'a]? I think both refer to same thing?
pub fn from_string() -> SecondStruct<'a> {
let obj = FirstStruct { a: 1 };
SecondStruct {
name: SecondStruct::extract_string(&obj),
}
}
This code says "I will return a SecondStruct with the lifetime 'a". The caller of the code gets to determine what the length of the lifetime 'a is. This is almost never what you want!
// Lifetime elision means the method is equivalent to this
// fn get<'a>(&'a self) -> Option<&'a str>
fn get(&self) -> Option<&str> {
Some("aaa")
}
This code uses says that the string returned will live as long as self lives.
Put these two concepts together, and you can understand your error. The variable obj is only defined to live as long as the function call is active. However, you are trying to return a reference to the inner-workings of the struct beyond the call! Actually, you are trying to return it for any arbitrary lifetime the caller decides! This is Rust preventing you from shooting yourself in the foot, hooray for Rust!
So how do you fix your problem? For the provided example code, the easiest thing is to just use the 'static lifetime:
struct FirstStruct { a: i8 }
impl FirstStruct {
fn get(&self) -> Option<&'static str> { Some("aaa") }
}
pub struct SecondStruct<'a> { name: Option<&'a str> }
impl<'a> SecondStruct<'a> {
fn extract_string(obj: &FirstStruct) -> Option<&'static str> { obj.get() }
pub fn from_string() -> SecondStruct<'static> {
let obj = FirstStruct { a: 1 };
SecondStruct { name: SecondStruct::extract_string(&obj) }
}
}
fn main() {
let g_def_res = SecondStruct::from_string();
}
But that's probably not what you really want. The next thing to try would be to embed FirstStruct inside SecondStruct, and simply delegate to it. Another option would be to move from &str to String - String owns the string data, and so you can transfer ownership from First to Second.
Whatever you do, you have to ensure that the source of the string data outlives the function call to from_string.
Either the return value of FirstStruct::get has been allocated on the stack or it has been allocated on the heap.
It's trickier than that. The return value is always on the stack. That is, the Option<&str> takes up space on the stack. The &str may contain a pointer to something that is allocated either on the stack or heap, it's not known by this code. All you know is that the pointed-at value is guaranteed to live for the lifetime of that specific FirstStruct item.
You don't own the string, so you can't transfer ownership around.
I can't move FirstStruct because it is from another library (rustc-serialize
I'm not sure what you mean. If you have an object, then you can embed it into your object. The fact that it comes from another crate doesn't come into play. If you have a reference to something, then you can still capture the reference, but then your object has to live for a shorter period than the reference (so that it never becomes invalid).
Unwrapping Option, updating to a string and rewrapping in Option is a lot of boilerplate.
Have you seen Option::map? It makes this kind of thing very succinct. Combined with From, you can write a very short thing to convert an Option<&str> to Option<String>:
// fn is just to establish some types, you'd just use the `.map` call in real code
fn foo(a: Option<&str>) -> Option<String> {
a.map(From::from)
}

Resources