Code:
#[derive(Debug)]
struct Haha {
data: i32,
}
use std::collections::BTreeMap;
fn main() {
let mut map: BTreeMap<i8, Option<Box<Haha>>> = BTreeMap::new();
map.insert(1, Some(Box::new(Haha { data: 3 })));
map.insert(2, None);
for (key, value) in map.iter_mut() {
if value.is_none() { // if find `None`, change it to a `Some(Haha)`
value = Some(Box::new(Haha { data: 5 }));
}
}
}
I want to make a function that when I get the value None in BTreeMap.value, I change it to Some value, not a reference. But it comes out a mistake:
error[E0308]: mismatched types
--> Untitled.rs:15:12
|
15 | value = Some(Box::new(Haha{data: 5}));
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected mutable reference, found enum `std::option::Option`
|
= note: expected type `&mut std::option::Option<std::boxed::Box<Haha>>`
found type `std::option::Option<std::boxed::Box<Haha>>`
= help: here are some functions which might fulfill your needs:
- .unwrap()
Because using map.iter_mut() I can only get the reference. How to send the true value Some(Haha) but not a reference to the mut reference value? If I change to value = & mut Some(Box::new(Haha { data: 5 })); Because Some(Box...) will be destructed, so it will come out another mistake.
Since value is a mutable reference (due to iter_mut()), just dereference it:
*value = Some(Box::new(Haha{data: 5}));
And it will work nicely.
Related
Background
To give a little bit of context: I have to mention that I am fairly new to Rust, so some of my questions might be really basic/silly - apologies for that! I am trying to implement a thin wrapper around rusqlite, which should allow to cache inserts to the database into a HashMap and then execute the actual inserts in a batched manner to improve performance of the inserts. The concept is taken from this post, in particular the batched version of the code.
To achieve this, I have thought of the following construct:
A struct DataBase, which holds some basic attributes of the database and a HashMap cache which contains the information of the cached tables
A struct CachedTable holding a vector with the field names and a vector of vectors holding the records to insert.
An enum SQLDataType enumerating a "Text" and "Integer" variant (for now)
The code looks like this:
pub enum SQLDataType {
Text(String),
Integer(isize),
}
#[derive(Debug)]
struct CachedTable {
data: RefCell<Vec<Vec<SQLDataType>>>,
fields: Vec<String>,
}
#[derive(Debug)]
pub struct DataBase {
name: String,
conn: Connection,
cache: HashMap<String, CachedTable>,
batch_size: usize,
}
Then I have a function commit_writes which does the follwoing:
Creates a vector of the tables in the cache
Loops through the tables vector and creates a prepared INSERT INTO statement based on the field names and the no of records (in the batched approach one needs to concatenate the list of value placeholders as many times as there are records to process within the VALUES() part of the statement.)
Creates the params vector
Call the statement.execute with the prepared params vector
Issue
I have tried a number of versions and got error messages about missing ToSql traits, livetime and borrowing errors, etc. After a bit of searching I found this stackoverflow question, but the code does not compile anymore and even if it would, it is handling a single data type (String) in the params vector. What I would like to achieve is to pass a vector of mixed data types (Strings and Integers for now) to the statement.execute function. My current version of commit_writes is as follows - the full code can be found here:
impl DataBase {
pub fn new(db_name: &str) -> Self {
//... snip ...//
}
fn add_to_cache(&mut self, table_name: &str, record: Vec<SQLDataType>) {
//... snip ...//
}
pub fn commit_writes(&mut self) {
// collect all keys to then iterate over the cache
// collecting all keys avoids the "move issue" of iterators
// over a mutable reference to the 'cache' HashMap
let mut tables: Vec<String> = Vec::new();
for key in self.cache.keys() {
tables.push(key.to_owned());
}
// process all cached tables and write to the DB
for table in &tables {
// only process cached tables that do contain data
let no_of_records = self.cache[table].data.borrow().len();
if no_of_records > 0 {
// create the field list
let field_list = self.cache[table].fields.join(", ");
// get the number of elements and create the params part of the SQL
let no_elems = self.cache[table].fields.len();
let params_string = vec!["?"; no_elems].join(", ").repeat(no_of_records);
// create the SQL statement and prepare it
let sql_ins = format!(
"INSERT INTO {} ({}) VALUES ({})",
table, field_list, params_string
);
let stmt = self.conn.prepare_cached(sql_ins.as_str()).unwrap();
// create the param values vector
let mut param_values: Vec<_> = Vec::new();
let mut int_value: isize = 0;
let mut string_value: String = "default".to_string();
for record in self.cache[table].data.borrow().iter() {
for item_value in record.iter() {
match item_value {
SQLDataType::Integer(v) => {
int_value = *v;
param_values.push(int_value as &dyn ToSql);
}
SQLDataType::Text(v) => {
string_value = *v;
param_values.push(string_value as &dyn ToSql);
}
}
}
}
// fianlly executed the batch of inserts
stmt.execute(&*param_values).unwrap();
// now clear the cached table's data
self.cache[table].data.borrow_mut().clear();
}
}
}
}
and this is the output of cargo check:
Checking utilrs v0.1.0 (C:\LocalData\Rust\utilrs)
error[E0605]: non-primitive cast: `isize` as `&dyn ToSql`
--> src\persistence.rs:104:51
|
104 | ... param_values.push(int_value as &dyn ToSql);
| ^^^^^^^^^^^^^^^^^^^^^^^ invalid cast
|
help: consider borrowing the value
|
104 | param_values.push(&int_value as &dyn ToSql);
| +
error[E0605]: non-primitive cast: `String` as `&dyn ToSql`
--> src\persistence.rs:108:51
|
108 | ... param_values.push(string_value as &dyn ToSql);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^ invalid cast
|
help: consider borrowing the value
|
108 | param_values.push(&string_value as &dyn ToSql);
| +
For more information about this error, try `rustc --explain E0605`.
error: could not compile `utilrs` due to 2 previous errors
But then if I add the borrow suggested by the compiler, this leads to the following errors related to the borrow checker:
error[E0506]: cannot assign to `int_value` because it is borrowed
--> src\persistence.rs:103:33
|
103 | ... int_value = *v;
| ^^^^^^^^^^^^^^ assignment to borrowed `int_value` occurs here
104 | ... param_values.push(&int_value as &dyn ToSql);
| -------------------------------------------
| | |
| | borrow of `int_value` occurs here
| borrow later used here
error[E0506]: cannot assign to `string_value` because it is borrowed
--> src\persistence.rs:107:33
|
107 | ... string_value = *v;
| ^^^^^^^^^^^^ assignment to borrowed `string_value` occurs here
108 | ... param_values.push(&string_value as &dyn ToSql);
| ----------------------------------------------
| | |
| | borrow of `string_value` occurs here
| borrow later used here
error[E0507]: cannot move out of `*v` which is behind a shared reference
--> src\persistence.rs:107:48
|
107 | ... string_value = *v;
| ^^ move occurs because `*v` has type `String`, which does not impent the `Copy` trait
error[E0596]: cannot borrow `stmt` as mutable, as it is not declared as mutable
--> src\persistence.rs:115:17
|
93 | let stmt = self.conn.prepare_cached(sql_ins.as_str()).unwrap();
| ---- help: consider changing this to be mutable: `mut stmt`
...
115 | stmt.execute(&*param_values).unwrap();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ cannot borrow as mutable
Some errors have detailed explanations: E0506, E0507, E0596.
For more information about an error, try `rustc --explain E0506`.
So I am going around in circles and would really appreciate any help with this!
I would suggest storing rusqlite::types::Value values directly in your struct instead of Box<dyn ToSql> like my solution in a comment above, which also works. Here's the new one:
let mut param_values: Vec<rusqlite::types::Value> = Vec::new();
for record in self.cache[table].data.iter() {
for item_value in record.iter() {
match item_value {
SQLDataType::Integer(v) => {
param_values.push((*v).into());
}
SQLDataType::Text(v) => {
param_values.push(v.clone().into());
}
}
}
}
Other than that, I went ahead and removed unnecessary RefCell uses and cleaned up some small things. The final code:
use rusqlite::Connection;
use std::collections::HashMap;
// See also https://stackoverflow.com/questions/40559931/vector-store-mixed-types-of-data-in-rust
#[derive(Debug)]
pub enum SQLDataType {
Text(String),
Integer(isize),
}
#[derive(Debug)]
struct CachedTable {
data: Vec<Vec<SQLDataType>>,
fields: Vec<String>,
}
#[derive(Debug)]
pub struct DataBase {
name: String,
conn: Connection,
cache: HashMap<String, CachedTable>,
batch_size: usize,
}
impl DataBase {
pub fn new(db_name: &str) -> Self {
let db_conn = Connection::open(db_name).unwrap();
let mut db = DataBase {
name: db_name.to_owned(),
conn: db_conn,
cache: HashMap::new(),
batch_size: 50,
};
db.cache.insert(
String::from("User"),
CachedTable {
data: Vec::new(),
fields: vec![
String::from("Name"),
String::from("Age"),
String::from("Gender"),
],
},
);
db
}
pub fn add_to_cache(&mut self, table_name: &str, record: Vec<SQLDataType>) {
if let Some(chached_table) = self.cache.get_mut(table_name) {
chached_table.data.push(record);
}
}
pub fn commit_writes(&mut self) {
// collect all keys to then iterate over the cache
// collecting all keys avoids the "move issue" of iterators
// over a mutable reference to the 'cache' HashMap
let mut tables: Vec<String> = Vec::new();
for key in self.cache.keys() {
tables.push(key.to_owned());
}
// process all cached tables and write to the DB
for table in &tables {
// only process cached tables that do contain data
let no_of_records = self.cache[table].data.len();
if no_of_records > 0 {
// create the field list
let field_list = self.cache[table].fields.join(", ");
// get the number of elements and create the params part of the SQL
let no_elems = self.cache[table].fields.len();
let params_string = vec!["?"; no_elems].join(", ").repeat(no_of_records);
// create the SQL statement and prepare it
let sql_ins = format!(
"INSERT INTO {} ({}) VALUES ({})",
table, field_list, params_string
);
let mut stmt = self.conn.prepare_cached(sql_ins.as_str()).unwrap();
// create the param values vector
let mut param_values: Vec<rusqlite::types::Value> = Vec::new();
for record in self.cache[table].data.iter() {
for item_value in record.iter() {
match item_value {
SQLDataType::Integer(v) => {
param_values.push((*v).into());
}
SQLDataType::Text(v) => {
param_values.push(v.clone().into());
}
}
}
}
// fianlly executed the batch of inserts
stmt.execute(rusqlite::params_from_iter(param_values))
.unwrap();
// now clear the cached table's data
self.cache.get_mut(table).unwrap().data.clear();
}
}
}
}
fn main() {
let mut db = DataBase::new("test.db");
let record: Vec<SQLDataType> = vec![
SQLDataType::Text("John Doe".to_string()),
SQLDataType::Integer(35),
SQLDataType::Text("male".to_string()),
];
db.add_to_cache("User", record);
db.commit_writes();
}
I am trying to pass around a HashMap which stores values through a set of nested enums/structs. The problem of multiple mutability happens during iteration, even all references should be dropped.
The general idea is to have a vector of values, iterate through them and simplify them, keeping track of them within the HashMap. There are two stages of simplification.
The general flow looks something like
run(Vec<ComplexVal>)
-for each val->
val.fix_complex(holder)
-for each `smp` SimpleVal in val->
basicval = Simplifier::step(smp, holder)
holder.insert("name", basicval)
But the problem is that the holder is borrowed mutably in each stage, and there isn't supposed to be any reference from the ComplexVal to the holder and since the borrowchecker doesn't like multiple borrows, it fails.
Full playground snippet: here
It happens in this snippet:
pub fn run(&mut self, mut vals: Vec<ComplexVal>) {
let mut holder = Holder{hold:HashMap::new()};
// .. setup holder code omitted
let len = vals.len();
for _ in 0..len {
let mut val = vals.remove(0); // remove from vec, should drop after running
println!("Running {:?}", val);
match val {
ComplexVal::Cmplx1(mut c) => {
c.fix_complex(&mut holder)
},
//... more cases of different types of values omitted for simplicity
}
// val *should* be dropped here, and therefore the mutable borrow of holder?
}
println!("Holder: {:?}", holder);
}
}
The only thing I can think of is that it somehow is related to the BasicVal::Ref(&BasicVal) value when created.
I need to return a reference of type &BasicVal so I can't use a regular fn() -> &BasicVal as the reference would be dangling, so I pass a ret value which is to be modified and used as the storage for the return value.
I have also tried just returning the enum BasicVal::Ref(&BasicVal), but run into the same mutability issues.
The example below is a much more simple version which (sort of) demonstrates the same error, just thought I'd include this context in case someone has another idea on how to implement this which wouldn't have these issues
Code (edited)
Updated playground link
Edit: I made a mistake in not needing the lifetimes of both holder and ret to explicitly be the same, so I have made an updated example for it
use std::borrow::BorrowMut;
///////////////////////////////
use std::cell::{RefCell, RefMut};
use std::collections::HashMap;
#[derive(Debug)]
enum BasicVal<'a> {
Ref(&'a BasicVal<'a>),
Val1(BasicStruct),
}
#[derive(Debug)]
struct Holder<'b> {
hold: HashMap<String, RefCell<BasicVal<'b>>>,
}
#[derive(Debug)]
struct BasicStruct {
val: i32,
}
impl<'a> BasicVal<'a> {
pub fn empty() -> Self { BasicVal::Val1(BasicStruct { val: 0 }) }
}
// must match sig of modify_val_ref
fn modify_val<'f>(holder: &'f mut Holder<'f>, mut ret: RefMut<BasicVal<'f>>) {
*ret = BasicVal::Val1(BasicStruct { val: 5 });
}
// must match sig of modify_val
fn modify_val_ref<'f>(holder: &'f mut Holder<'f>, mut ret: RefMut<BasicVal<'f>>) {
ret = holder.hold.get("reference_val").unwrap().borrow_mut();
}
fn do_modify<'f>(holder: &'f mut Holder<'f>) {
let mut v = RefCell::new(BasicVal::empty());
println!("Original {:?}", v);
modify_val(holder, v.borrow_mut());
holder.hold.insert("Data".to_string(), v);
println!("Modified {:?}", holder.hold.get("Data"));
}
pub fn test_dropborrow() {
let mut holder = Holder { hold: HashMap::new() };
holder.hold.insert(
"reference_val".to_string(),
RefCell::new(BasicVal::Val1(BasicStruct { val: 8 })),
);
do_modify(&mut holder);
}
pub fn main() {
test_dropborrow();
}
Edit: Using just the holder for a temp return value gives me a multiple mutable borrow issue, so that workaround doesn't work. I have also tried it with a RefCell with the same issue.
fn modify_val<'f>(holder: &'f mut Holder<'f>) {
holder.hold.insert("$return".to_string(), BasicVal::Val1(BasicStruct{val: 5}));
}
fn do_modify<'f>(holder: &'f mut Holder<'f>) {
modify_val(holder);
let mut v = holder.hold.remove("$return").unwrap();
holder.hold.insert("Data".to_string(), v);
println!("Modified {:?}", v);
}
Error:
935 | fn do_modify<'f>(holder: &'f mut Holder<'f>) {
| -- lifetime `'f` defined here
936 |
937 | modify_val(holder);
| ------------------
| | |
| | first mutable borrow occurs here
| argument requires that `*holder` is borrowed for `'f`
938 | let mut v = holder.hold.remove("$return").unwrap();
| ^^^^^^^^^^^ second mutable borrow occurs here
Any help is greatly appreciated!!!
Figured it out, essentially the BasicVal<'a> was causing Holder to mutably borrow itself in successive iterations of the loop, so removing the lifetime was pretty much the only solution
I'm new to Rust and I'm struggle with the concept of lifetimes. I want to make a struct that iterates through a file a character at a time, but I'm running into issues where I need lifetimes. I've tried to add them where I thought they should be but the compiler isn't happy. Here's my code:
struct Advancer<'a> {
line_iter: Lines<BufReader<File>>,
char_iter: Chars<'a>,
current: Option<char>,
peek: Option<char>,
}
impl<'a> Advancer<'a> {
pub fn new(file: BufReader<File>) -> Result<Self, Error> {
let mut line_iter = file.lines();
if let Some(Ok(line)) = line_iter.next() {
let char_iter = line.chars();
let mut advancer = Advancer {
line_iter,
char_iter,
current: None,
peek: None,
};
// Prime the pump. Populate peek so the next call to advance returns the first char
let _ = advancer.next();
Ok(advancer)
} else {
Err(anyhow!("Failed reading an empty file."))
}
}
pub fn next(&mut self) -> Option<char> {
self.current = self.peek;
if let Some(char) = self.char_iter.next() {
self.peek = Some(char);
} else {
if let Some(Ok(line)) = self.line_iter.next() {
self.char_iter = line.chars();
self.peek = Some('\n');
} else {
self.peek = None;
}
}
self.current
}
pub fn current(&self) -> Option<char> {
self.current
}
pub fn peek(&self) -> Option<char> {
self.peek
}
}
fn main() -> Result<(), Error> {
let file = File::open("input_file.txt")?;
let file_buf = BufReader::new(file);
let mut advancer = Advancer::new(file_buf)?;
while let Some(char) = advancer.next() {
print!("{}", char);
}
Ok(())
}
And here's what the compiler is telling me:
error[E0515]: cannot return value referencing local variable `line`
--> src/main.rs:37:13
|
25 | let char_iter = line.chars();
| ---- `line` is borrowed here
...
37 | Ok(advancer)
| ^^^^^^^^^^^^ returns a value referencing data owned by the current function
error[E0597]: `line` does not live long enough
--> src/main.rs:49:34
|
21 | impl<'a> Advancer<'a> {
| -- lifetime `'a` defined here
...
49 | self.char_iter = line.chars();
| -----------------^^^^--------
| | |
| | borrowed value does not live long enough
| assignment requires that `line` is borrowed for `'a`
50 | self.peek = Some('\n');
51 | } else {
| - `line` dropped here while still borrowed
error: aborting due to 2 previous errors
Some errors have detailed explanations: E0515, E0597.
For more information about an error, try `rustc --explain E0515`.
error: could not compile `advancer`.
Some notes:
The Chars iterator borrows from the String it was created from. So you can't drop the String while the iterator is alive. But that's what happens in your new() method, the line variable owning the String disappears while the iterator referencing it is stored in the struct.
You could also try storing the current line in the struct, then it would live long enough, but that's not an option – a struct cannot hold a reference to itself.
Can you make a char iterator on a String that doesn't store a reference into the String? Yes, probably, for instance by storing the current position in the string as an integer – it shouldn't be the index of the char, because chars can be more than one byte long, so you'd need to deal with the underlying bytes yourself (using e.g. is_char_boundary() to take the next bunch of bytes starting from your current index that form a char).
Is there an easier way? Yes, if performance is not of highest importance, one solution is to make use of Vec's IntoIterator instance (which uses unsafe magic to create an object that hands out parts of itself) :
let char_iter = file_buf.lines().flat_map(|line_res| {
let line = line_res.unwrap_or(String::new());
line.chars().collect::<Vec<_>>()
});
Note that just returning line.chars() would have the same problem as the first point.
You might think that String should have a similar IntoIterator instance, and I wouldn't disagree.
I am learning Rust and I don't quite get why this is not working.
#[derive(Debug)]
struct Node {
value: String,
}
#[derive(Debug)]
pub struct Graph {
nodes: Vec<Box<Node>>,
}
fn mk_node(value: String) -> Node {
Node { value }
}
pub fn mk_graph() -> Graph {
Graph { nodes: vec![] }
}
impl Graph {
fn add_node(&mut self, value: String) {
if let None = self.nodes.iter().position(|node| node.value == value) {
let node = Box::new(mk_node(value));
self.nodes.push(node);
};
}
fn get_node_by_value(&self, value: &str) -> Option<&Node> {
match self.nodes.iter().position(|node| node.value == *value) {
None => None,
Some(idx) => self.nodes.get(idx).map(|n| &**n),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn some_test() {
let mut graph = mk_graph();
graph.add_node("source".to_string());
graph.add_node("destination".to_string());
let source = graph.get_node_by_value("source").unwrap();
let dest = graph.get_node_by_value("destination").unwrap();
graph.add_node("destination".to_string());
}
}
(playground)
This has the error
error[E0502]: cannot borrow `graph` as mutable because it is also borrowed as immutable
--> src/main.rs:50:9
|
47 | let source = graph.get_node_by_value("source").unwrap();
| ----- immutable borrow occurs here
...
50 | graph.add_node("destination".to_string());
| ^^^^^ mutable borrow occurs here
51 | }
| - immutable borrow ends here
This example from Programming Rust is quite similar to what I have but it works:
pub struct Queue {
older: Vec<char>, // older elements, eldest last.
younger: Vec<char>, // younger elements, youngest last.
}
impl Queue {
/// Push a character onto the back of a queue.
pub fn push(&mut self, c: char) {
self.younger.push(c);
}
/// Pop a character off the front of a queue. Return `Some(c)` if there /// was a character to pop, or `None` if the queue was empty.
pub fn pop(&mut self) -> Option<char> {
if self.older.is_empty() {
if self.younger.is_empty() {
return None;
}
// Bring the elements in younger over to older, and put them in // the promised order.
use std::mem::swap;
swap(&mut self.older, &mut self.younger);
self.older.reverse();
}
// Now older is guaranteed to have something. Vec's pop method // already returns an Option, so we're set.
self.older.pop()
}
pub fn split(self) -> (Vec<char>, Vec<char>) {
(self.older, self.younger)
}
}
pub fn main() {
let mut q = Queue {
older: Vec::new(),
younger: Vec::new(),
};
q.push('P');
q.push('D');
assert_eq!(q.pop(), Some('P'));
q.push('X');
let (older, younger) = q.split(); // q is now uninitialized.
assert_eq!(older, vec!['D']);
assert_eq!(younger, vec!['X']);
}
A MRE of your problem can be reduced to this:
// This applies to the version of Rust this question
// was asked about; see below for updated examples.
fn main() {
let mut items = vec![1];
let item = items.last();
items.push(2);
}
error[E0502]: cannot borrow `items` as mutable because it is also borrowed as immutable
--> src/main.rs:4:5
|
3 | let item = items.last();
| ----- immutable borrow occurs here
4 | items.push(2);
| ^^^^^ mutable borrow occurs here
5 | }
| - immutable borrow ends here
You are encountering the exact problem that Rust was designed to prevent. You have a reference pointing into the vector and are attempting to insert into the vector. Doing so might require that the memory of the vector be reallocated, invalidating any existing references. If that happened and you used the value in item, you'd be accessing uninitialized memory, potentially causing a crash.
In this particular case, you aren't actually using item (or source, in the original) so you could just... not call that line. I assume you did that for some reason, so you could wrap the references in a block so that they go away before you try to mutate the value again:
fn main() {
let mut items = vec![1];
{
let item = items.last();
}
items.push(2);
}
This trick is no longer needed in modern Rust because non-lexical lifetimes have been implemented, but the underlying restriction still remains — you cannot have a mutable reference while there are other references to the same thing. This is one of the rules of references covered in The Rust Programming Language. A modified example that still does not work with NLL:
let mut items = vec![1];
let item = items.last();
items.push(2);
println!("{:?}", item);
In other cases, you can copy or clone the value in the vector. The item will no longer be a reference and you can modify the vector as you see fit:
fn main() {
let mut items = vec![1];
let item = items.last().cloned();
items.push(2);
}
If your type isn't cloneable, you can transform it into a reference-counted value (such as Rc or Arc) which can then be cloned. You may or may not also need to use interior mutability:
struct NonClone;
use std::rc::Rc;
fn main() {
let mut items = vec![Rc::new(NonClone)];
let item = items.last().cloned();
items.push(Rc::new(NonClone));
}
this example from Programming Rust is quite similar
No, it's not, seeing as how it doesn't use references at all.
See also
Cannot borrow `*x` as mutable because it is also borrowed as immutable
Pushing something into a vector depending on its last element
Why doesn't the lifetime of a mutable borrow end when the function call is complete?
How should I restructure my graph code to avoid an "Cannot borrow variable as mutable more than once at a time" error?
Why do I get the error "cannot borrow x as mutable more than once"?
Why does Rust want to borrow a variable as mutable more than once at a time?
Try to put your immutable borrow inside a block {...}.
This ends the borrow after the block.
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn some_test() {
let mut graph = mk_graph();
graph.add_node("source".to_string());
graph.add_node("destination".to_string());
{
let source = graph.get_node_by_value("source").unwrap();
let dest = graph.get_node_by_value("destination").unwrap();
}
graph.add_node("destination".to_string());
}
}
So for anyone else banging their head against this problem and wanting a quick way out - use clones instead of references. Eg I'm iterating this list of cells and want to change an attribute so I first copy the list:
let created = self.cells
.into_iter()
.map(|c| {
BoardCell {
x: c.x,
y: c.y,
owner: c.owner,
adjacency: c.adjacency.clone(),
}
})
.collect::<Vec<BoardCell>>();
And then modify the values in the original by looping the copy:
for c in created {
self.cells[(c.x + c.y * self.size) as usize].adjacency[dir] = count;
}
Using Vec<&BoardCell> would just yield this error. Not sure how Rusty this is but hey, it works.
I am learning Rust and I don't quite get why this is not working.
#[derive(Debug)]
struct Node {
value: String,
}
#[derive(Debug)]
pub struct Graph {
nodes: Vec<Box<Node>>,
}
fn mk_node(value: String) -> Node {
Node { value }
}
pub fn mk_graph() -> Graph {
Graph { nodes: vec![] }
}
impl Graph {
fn add_node(&mut self, value: String) {
if let None = self.nodes.iter().position(|node| node.value == value) {
let node = Box::new(mk_node(value));
self.nodes.push(node);
};
}
fn get_node_by_value(&self, value: &str) -> Option<&Node> {
match self.nodes.iter().position(|node| node.value == *value) {
None => None,
Some(idx) => self.nodes.get(idx).map(|n| &**n),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn some_test() {
let mut graph = mk_graph();
graph.add_node("source".to_string());
graph.add_node("destination".to_string());
let source = graph.get_node_by_value("source").unwrap();
let dest = graph.get_node_by_value("destination").unwrap();
graph.add_node("destination".to_string());
}
}
(playground)
This has the error
error[E0502]: cannot borrow `graph` as mutable because it is also borrowed as immutable
--> src/main.rs:50:9
|
47 | let source = graph.get_node_by_value("source").unwrap();
| ----- immutable borrow occurs here
...
50 | graph.add_node("destination".to_string());
| ^^^^^ mutable borrow occurs here
51 | }
| - immutable borrow ends here
This example from Programming Rust is quite similar to what I have but it works:
pub struct Queue {
older: Vec<char>, // older elements, eldest last.
younger: Vec<char>, // younger elements, youngest last.
}
impl Queue {
/// Push a character onto the back of a queue.
pub fn push(&mut self, c: char) {
self.younger.push(c);
}
/// Pop a character off the front of a queue. Return `Some(c)` if there /// was a character to pop, or `None` if the queue was empty.
pub fn pop(&mut self) -> Option<char> {
if self.older.is_empty() {
if self.younger.is_empty() {
return None;
}
// Bring the elements in younger over to older, and put them in // the promised order.
use std::mem::swap;
swap(&mut self.older, &mut self.younger);
self.older.reverse();
}
// Now older is guaranteed to have something. Vec's pop method // already returns an Option, so we're set.
self.older.pop()
}
pub fn split(self) -> (Vec<char>, Vec<char>) {
(self.older, self.younger)
}
}
pub fn main() {
let mut q = Queue {
older: Vec::new(),
younger: Vec::new(),
};
q.push('P');
q.push('D');
assert_eq!(q.pop(), Some('P'));
q.push('X');
let (older, younger) = q.split(); // q is now uninitialized.
assert_eq!(older, vec!['D']);
assert_eq!(younger, vec!['X']);
}
A MRE of your problem can be reduced to this:
// This applies to the version of Rust this question
// was asked about; see below for updated examples.
fn main() {
let mut items = vec![1];
let item = items.last();
items.push(2);
}
error[E0502]: cannot borrow `items` as mutable because it is also borrowed as immutable
--> src/main.rs:4:5
|
3 | let item = items.last();
| ----- immutable borrow occurs here
4 | items.push(2);
| ^^^^^ mutable borrow occurs here
5 | }
| - immutable borrow ends here
You are encountering the exact problem that Rust was designed to prevent. You have a reference pointing into the vector and are attempting to insert into the vector. Doing so might require that the memory of the vector be reallocated, invalidating any existing references. If that happened and you used the value in item, you'd be accessing uninitialized memory, potentially causing a crash.
In this particular case, you aren't actually using item (or source, in the original) so you could just... not call that line. I assume you did that for some reason, so you could wrap the references in a block so that they go away before you try to mutate the value again:
fn main() {
let mut items = vec![1];
{
let item = items.last();
}
items.push(2);
}
This trick is no longer needed in modern Rust because non-lexical lifetimes have been implemented, but the underlying restriction still remains — you cannot have a mutable reference while there are other references to the same thing. This is one of the rules of references covered in The Rust Programming Language. A modified example that still does not work with NLL:
let mut items = vec![1];
let item = items.last();
items.push(2);
println!("{:?}", item);
In other cases, you can copy or clone the value in the vector. The item will no longer be a reference and you can modify the vector as you see fit:
fn main() {
let mut items = vec![1];
let item = items.last().cloned();
items.push(2);
}
If your type isn't cloneable, you can transform it into a reference-counted value (such as Rc or Arc) which can then be cloned. You may or may not also need to use interior mutability:
struct NonClone;
use std::rc::Rc;
fn main() {
let mut items = vec![Rc::new(NonClone)];
let item = items.last().cloned();
items.push(Rc::new(NonClone));
}
this example from Programming Rust is quite similar
No, it's not, seeing as how it doesn't use references at all.
See also
Cannot borrow `*x` as mutable because it is also borrowed as immutable
Pushing something into a vector depending on its last element
Why doesn't the lifetime of a mutable borrow end when the function call is complete?
How should I restructure my graph code to avoid an "Cannot borrow variable as mutable more than once at a time" error?
Why do I get the error "cannot borrow x as mutable more than once"?
Why does Rust want to borrow a variable as mutable more than once at a time?
Try to put your immutable borrow inside a block {...}.
This ends the borrow after the block.
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn some_test() {
let mut graph = mk_graph();
graph.add_node("source".to_string());
graph.add_node("destination".to_string());
{
let source = graph.get_node_by_value("source").unwrap();
let dest = graph.get_node_by_value("destination").unwrap();
}
graph.add_node("destination".to_string());
}
}
So for anyone else banging their head against this problem and wanting a quick way out - use clones instead of references. Eg I'm iterating this list of cells and want to change an attribute so I first copy the list:
let created = self.cells
.into_iter()
.map(|c| {
BoardCell {
x: c.x,
y: c.y,
owner: c.owner,
adjacency: c.adjacency.clone(),
}
})
.collect::<Vec<BoardCell>>();
And then modify the values in the original by looping the copy:
for c in created {
self.cells[(c.x + c.y * self.size) as usize].adjacency[dir] = count;
}
Using Vec<&BoardCell> would just yield this error. Not sure how Rusty this is but hey, it works.