What is the correct way to pass references? - rust

I have a function that does multiple operations on two vectors. The vectors consist of structs that are later converted to tuples etc....
What I would like is to change the value of a struct in vector 1 by reference, so that the value in vector 1 is equal to the value in vector 2.
When you run the program, you will better see, what I mean.
You'll get the following two output lines:
new_rows after: IndexSet { rows: [IndexRow { is_old_idx: false, hash: "1", value: [], name: "", viewentry_id: "yyy" }] }
old_rows after: IndexSet { rows: [IndexRow { is_old_idx: true, hash: "1", value: [], name: "", viewentry_id: "xxx" }] }
And what I would like is that in new_rows_after.rows.viewentry_id there is also an "xxx". But it still contains the original value "yyy".
At some point I don't seem to pass the reference correctly, but I just can't find the place.
Is there perhaps an experienced Rust expert here who can see where the error might be?
Thanks for your help.
Playground link

If I may say that, your code is pretty messy. I mean, if you want us to help you, you could at least try to make it easier for us to help. One thing that generally helps is: try to reduce the length of your example code. Maybe this can help you to come up with a minimal version of your code in the future: http://blog.pnkfx.org/blog/2019/11/18/rust-bug-minimization-patterns/
(it is actually about minimal code examples for ICE, but minimizing Rust examples is really a general thing, if you ask me).
Problem
Now, to the actual problem. I don't really understand your full example code, so what I did, I just reduce it from a whooping 194 lines down to 43 lines, just keeping, what I assume, is your actual problem.
Also, I found your variable names rather confusing. I mean, what I understand that your problem is about, is that new should have the value from old but instead new has its old value instead of the new value of old -- seriously, that is just confusing. So, I went with a simple foo and bar, and here we go:
fn main() {
let dummy_foo = vec![IndexRow {
id: "good".to_string(),
}];
let dummy_bar = vec![IndexRow {
id: "bad".to_string(),
}];
let set_foo = IndexSet { rows: dummy_foo };
let mut set_bar = IndexSet { rows: dummy_bar };
// Should copy ids from set_foo to set_bar
copy_ids(&mut set_bar, &set_foo);
// Here set_bar still contains "bad"
println!("set_bar: {:?}", set_bar);
}
#[derive(Debug)]
pub struct IndexRow {
pub id: String,
}
#[derive(Debug)]
pub struct IndexSet {
pub rows: Vec<IndexRow>,
}
/// Copy ids from `src` to `dest`
pub fn copy_ids<'a, 'b>(dest: &'a mut IndexSet, src: &'b IndexSet) {
// Create tuples each with a dest and src entry
let mut tuples: Vec<(&str, &str)> = dest
.rows
.iter()
.zip(src.rows.iter())
.map(|(d, s)| (d.id.as_str(), s.id.as_str()))
.collect();
for t in tuples.iter_mut() {
let (ref mut dest_id, src_id) = t;
// Override dest with src
*dest_id = *src_id;
}
}
playground
Now as I understand it, in this above version, the issue is just that the id in set_bar should be replaced with the id in set_foo, but instead set_bar still contains the old "bad" as it is printed at the end of main.
Solution
Assuming that this in deed the case: the problem is rather simple. You need to actually change the id, which is a String. However, in the tuples variable, you have only immutable (&) strs. Therefore, *dest_id = *src_id just replaces the one reference with another and all that is only stored/modified within tuples. The actual String is never touched, it is not even accessible as such from tuples.
So, what you need to do is: get your self access to a modifiable (&mut) String and then modify that string directly. Here you can either replace the entire string e.g. with *dest_id = src_id.to_string(), or if you want to make sure that you really have a String on the left-hand side, you can call a function on it that only exists on String and not on str like dest_id.replace_range(.., src_id).
So, this version of copy_ids does what it should do:
/// Copy ids from `src` to `dest`
pub fn copy_ids<'a, 'b>(dest: &'a mut IndexSet, src: &'b IndexSet) {
// Create tuples each with a dest and src entry
let tuples: Vec<(&mut String, &str)> = dest
.rows
.iter_mut()
.zip(src.rows.iter())
.map(|(d, s)| (&mut d.id, s.id.as_str()))
.collect();
// Override dest with src
for (dest_id, src_id) in tuples.into_iter() {
// Replace the content of the String
dest_id.replace_range(.., src_id);
}
}
full example on playground

Related

struct with reference to element of a vector in another field

I have the below example where I want a struct which holds a vector of data in one field, and has another field which contains the currently selected field. My understanding is that this is not possible in rust because I could remove the element in tables which selected points to, thereby creating a dangling pointer (can't borrow mut when an immutable borrow exists). The obvious workaround is to instead store a usize index for the element, rather than a &'a String. But this means I need to update the index if I remove an element from tables. Is there any way to avoid this using smart pointers, or just any better solutions in general? I've looked at other questions but they are not quite the same as below, and have extra information which makes them harder to follow for a beginner like myself, whereas below is a very minimal example.
struct Data<'a> {
selected: &'a String,
tables: Vec<String>,
}
fn main() {
let tables = vec!["table1".to_string(), "table2".to_string()];
let my_stuff = Data {
selected: &tables[0],
tables: tables,
};
}
You quite rightfully assessed that the way you wrote it is not possible, because Rust guarantees memory safety and storing it as a reference would give the possibility to create a dangling pointer.
There are several solutions that I could see here.
Static Strings
This of course only works if you store compile-time static strings.
struct Data {
selected: &'static str,
tables: Vec<&'static str>,
}
fn main() {
let tables = vec!["table1", "table2"];
let my_stuff = Data {
selected: &tables[0],
tables,
};
}
The reason this works is because static strings are non-mutable and guaranteed to never be deallocated. Also, in case this is confusing, I recommend reading up on the differences between Strings and str slices.
You can even go one further and reduce the lifetime down to 'a. But then, you have to store them as &'a str in the vector, to ensure they cannot be edited.
But that then allows you to store Strings in them, as long as the strings can be borrowed for the entire lifetime of the Data object.
struct Data<'a> {
selected: &'a str,
tables: Vec<&'a str>,
}
fn main() {
let str1 = "table1".to_string();
let str2 = "table2".to_string();
let tables = vec![str1.as_str(), str2.as_str()];
let my_stuff = Data {
selected: &tables[0],
tables,
};
}
Reference counting smart pointers
Depending your situation, there are several types that are recommended:
Rc<...> - if your data is immutable. Otherwise, you need to create interior mutability with:
Rc<Cell<...>> - safest and best solution IF your problem is single-threaded and deals with simple data types
Rc<RefCell<...>> - for more complex data types that have to be updated in-place and can't just be moved in and out
Arc<Mutex<...>> - as soon as your problem stretches over multiple threads
In your case, the data is in fact simple and your program is single-threaded, so I'd go with:
use std::{cell::Cell, rc::Rc};
struct Data {
selected: Rc<Cell<String>>,
tables: Vec<Rc<Cell<String>>>,
}
fn main() {
let tables = vec![
Rc::new(Cell::new("table1".to_string())),
Rc::new(Cell::new("table2".to_string())),
];
let my_stuff = Data {
selected: tables[0].clone(),
tables,
};
}
Of course, if you don't want to modify your strings after creation, you could go with:
use std::rc::Rc;
struct Data {
selected: Rc<String>,
tables: Vec<Rc<String>>,
}
fn main() {
let tables = vec![Rc::new("table1".to_string()), Rc::new("table2".to_string())];
let my_stuff = Data {
selected: tables[0].clone(),
tables,
};
}
Hiding the data structure and using an index
As you already mentioned, you could use an index instead. Then you would have to hide the vector and provide getters/setters/modifiers to make sure the index is kept in sync when the vector changes.
I'll keep the implementation up to the reader and won't provide an example here :)
I hope this helped already, or at least gave you a couple of new ideas. I'm happy to see new people come to the community, so feel free to ask further questions if you have any :)

How to use fold without making the list mutable?

I have provided an example here:
#[derive(Default)]
struct Example {
name: String,
description: String,
}
fn main() {
let lines = vec![
"N: N1",
"D: D1",
"",
"N: N2",
"D: D2",
"",
];
let data = lines.into_iter().fold(Vec::<Example>::new(), |acc, line| {
let mut examples = acc;
match line.chars().collect::<Vec<char>>().as_slice() {
['N', ..] => {
let mut element:Example = Default::default();
element.name = line[2..].into();
examples.push(element);
}
['D', ..] => {
let mut element = examples.pop().unwrap();
element.description = line[2..].into();
examples.push(element);
}
&[_, ..] => {}
&[] => {}
}
return examples;
});
for example in data{
println!("Name: {}, Description: {}", example.name, example.description);
}
}
Playground
Basically, I will be processing a steam of lines (the amount unknown at runtime, I have used an array here for the purpose of the example) and I want to build up a struct with the information and when I reach a given termination point, I start a new struct and add it to the list.
My first attempts at this used an outer most mutable list. I then discovered the fold method which seemed more elegant (IMO) but I still have to make the list mutable inside.
What would be a better way of achieving this and/or how could I remove the need to make the list mutable?
If you always have this same structure (only 2 fields, 3 rows per record), and you can have 2 independent iterators over the data,
it is possible to do a trick:
let names = lines.iter();
let descriptions = lines.iter().skip(1);
let name_desc_pairs = names.zip(descriptions).step_by(3);
let examples = name_desc_pairs.map(parse_example);
Where fn parse_example(lines: (&String, &String)) -> Example would take a pair of (name_line, description_line) strings and construct an Example.
Otherwise if you want arbitrary number of fields, consider that while you iterate over lines, at first you only get a partial example, so some buffering of the partial state is needed. There are no methods for that in the standard Iterator.
There's chunks method in the futures crate if you can use that: stream::iter(lines).chunks(3) spits out vectors of 3 lines, each of which you can parse into an Example.
Without that it's possible to implement your own buffering & parsing Iterator.
The idea is that the iterator state contains a partial example, e.g.:
struct ExampleBuilder {
name: Option<String>,
description: Option<String>,
}
and wraps the original iterator. In its next() method it calls next() of the original iterator a few times, and either adds line data to ExampleBuilder, or when it gets "" separator - converts ExampleBuilder to Example and returns it.

Return exact value in Rust HashMap

I can't find a suitable way to return the exact value of key in a HashMap in Rust . All the existing get methods return in a different format rather than the exact format.
You probably want the HashMap::remove method - it deletes the key from the map and returns the original value rather than a reference:
use std::collections::HashMap;
struct Thing {
content: String,
}
fn main() {
let mut hm: HashMap<u32, Thing> = HashMap::new();
hm.insert(
123,
Thing {
content: "abc".into(),
},
);
hm.insert(
432,
Thing {
content: "def".into(),
},
);
// Remove object from map, and take ownership of it
let value = hm.remove(&432);
if let Some(v) = value {
println!("Took ownership of Thing with content {:?}", v.content);
};
}
The get methods must return a reference to the object because the original object can only exist in one place (it is owned by the HashMap). The remove method can return the original object (i.e "take ownership") only because it removes it from its original owner.
Another solution, depending on the specific situation, may be to take the reference, call .clone() on it to make a new copy of the object (in this case it wouldn't work because Clone isn't implemented for our Thing example object - but it would work if the value way, say, a String)
Finally it may be worth noting you can still use the reference to the object in many circumstances - e.g the previous example could be done by getting a reference:
use std::collections::HashMap;
struct Thing {
content: String,
}
fn main() {
let mut hm: HashMap<u32, Thing> = HashMap::new();
hm.insert(
123,
Thing {
content: "abc".into(),
},
);
hm.insert(
432,
Thing {
content: "def".into(),
},
);
let value = hm.get(&432); // Get reference to the Thing containing "def" instead of removing it from the map and taking ownership
// Print the `content` as in previous example.
if let Some(v) = value {
println!("Showing content of referenced Thing: {:?}", v.content);
}
}
There are two basic methods of obtaining the value for the given key: get() and get_mut(). Use the first one if you just want to read the value, and the second one if you need to modify the value:
fn get(&self, k: &Q) -> Option<&V>
fn get_mut(&mut self, k: &Q) -> Option<&mut V>
As you can see from their signatures, both of these methods return Option rather than a direct value. The reason is that there may be no value associated to the given key:
use std::collections::HashMap;
let mut map = HashMap::new();
map.insert(1, "a");
assert_eq!(map.get(&1), Some(&"a")); // key exists
assert_eq!(map.get(&2), None); // key does not exist
If you are sure that the map contains the given key, you can use unwrap() to get the value out of the option:
assert_eq!(map.get(&1).unwrap(), &"a");
However, in general, it is better (and safer) to consider also the case when the key might not exist. For example, you may use pattern matching:
if let Some(value) = map.get(&1) {
assert_eq!(value, &"a");
} else {
// There is no value associated to the given key.
}

Lifetime of references in closures

I need a closure to refer to parts of an object in its enclosing environment. The object is created within the environment and is scoped to it, but once created it could be safely moved to the closure.
The use case is a function that does some preparatory work and returns a closure that will do the rest of the work. The reason for this design are execution constraints: the first part of the work involves allocation, and the remainder must do no allocation. Here is a minimal example:
fn stage_action() -> Box<Fn() -> ()> {
// split a freshly allocated string into pieces
let string = String::from("a:b:c");
let substrings = vec![&string[0..1], &string[2..3], &string[4..5]];
// the returned closure refers to the subtrings vector of
// slices without any further allocation or modification
Box::new(move || {
for sub in substrings.iter() {
println!("{}", sub);
}
})
}
fn main() {
let action = stage_action();
// ...executed some time later:
action();
}
This fails to compile, correctly stating that &string[0..1] and others must not outlive string. But if string were moved into the closure, there would be no problem. Is there a way to force that to happen, or another approach that would allow the closure to refer to parts of an object created just outside of it?
I've also tried creating a struct with the same functionality to make the move fully explicit, but that doesn't compile either. Again, compilation fails with the error that &later[0..1] and others only live until the end of function, but "borrowed value must be valid for the static lifetime".
Even completely avoiding a Box doesn't appear to help - the compiler complains that the object doesn't live long enough.
There's nothing specific to closures here; it's the equivalent of:
fn main() {
let string = String::from("a:b:c");
let substrings = vec![&string[0..1], &string[2..3], &string[4..5]];
let string = string;
}
You are attempting to move the String while there are outstanding borrows. In my example here, it's to another variable; in your example it's to the closure's environment. Either way, you are still moving it.
Additionally, you are trying to move the substrings into the same closure environment as the owning string. That's makes the entire problem equivalent to Why can't I store a value and a reference to that value in the same struct?:
struct Environment<'a> {
string: String,
substrings: Vec<&'a str>,
}
fn thing<'a>() -> Environment<'a> {
let string = String::from("a:b:c");
let substrings = vec![&string[0..1], &string[2..3], &string[4..5]];
Environment {
string: string,
substrings: substrings,
}
}
The object is created within the environment and is scoped to it
I'd disagree; string and substrings are created outside of the closure's environment and moved into it. It's that move that's tripping you up.
once created it could be safely moved to the closure.
In this case that's true, but only because you, the programmer, can guarantee that the address of the string data inside the String will remain constant. You know this for two reasons:
String is internally implemented with a heap allocation, so moving the String doesn't move the string data.
The String will never be mutated, which could cause the string to reallocate, invalidating any references.
The easiest solution for your example is to simply convert the slices to Strings and let the closure own them completely. This may even be a net benefit if that means you can free a large string in favor of a few smaller strings.
Otherwise, you meet the criteria laid out under "There is a special case where the lifetime tracking is overzealous" in Why can't I store a value and a reference to that value in the same struct?, so you can use crates like:
owning_ref
use owning_ref::RcRef; // 0.4.1
use std::rc::Rc;
fn stage_action() -> impl Fn() {
let string = RcRef::new(Rc::new(String::from("a:b:c")));
let substrings = vec![
string.clone().map(|s| &s[0..1]),
string.clone().map(|s| &s[2..3]),
string.clone().map(|s| &s[4..5]),
];
move || {
for sub in &substrings {
println!("{}", &**sub);
}
}
}
fn main() {
let action = stage_action();
action();
}
ouroboros
use ouroboros::self_referencing; // 0.2.3
fn stage_action() -> impl Fn() {
#[self_referencing]
struct Thing {
string: String,
#[borrows(string)]
substrings: Vec<&'this str>,
}
let thing = ThingBuilder {
string: String::from("a:b:c"),
substrings_builder: |s| vec![&s[0..1], &s[2..3], &s[4..5]],
}
.build();
move || {
thing.with_substrings(|substrings| {
for sub in substrings {
println!("{}", sub);
}
})
}
}
fn main() {
let action = stage_action();
action();
}
Note that I'm no expert user of either of these crates, so these examples may not be the best use of it.

error: use of moved value - should I use "&" or "mut" or something else?

My code:
enum MyEnum1 {
//....
}
struct Struct1 {
field1: MyEnum1,
field2: String
}
fn fn1(a: Struct1, b: String, c: String) -> String {
let let1 = fn2(a.field1);
let let2 = fn3(let1, b, c);
format!("{} something 123 {}", let1, let2)
}
fn fn2(a: MyEnum1) -> String {
//....
}
fn fn3(a: MyEnum1, b: Struct1) -> String {
//....
}
error: use of moved value: `a.field1`
error: use of moved value: `let1`
How can I fix them? Should I add & to the parameters of 'fn2andfn3? Ormut`? I can't understand the idea of how to fix these kind of errors.
These errors come from the most important concept in Rust - ownership. You should read the official book, especially the chapter on ownership - this would help you understand "how tho fix this kind of errors".
In short, specifically in your code, the problem is that String is a non-copyable type, that is, String values are not copied when passed to functions or assigned to local variables, they are moved. This means that wherever they were before, they are not accessible from there anymore.
Let's look at your function:
enum MyEnum1 {
//....
}
struct Struct1 {
field1: MyEnum1,
field2: String
}
fn fn1(a: Struct1, b: String, c: String) -> String {
let let1 = fn2(a.field1);
let let2 = fn3(let1, b, c);
format!("{} something 123 {}", let1, let2)
}
fn fn2(a: MyEnum1) -> String {
//....
}
All types here are not automatically copyable (they don't implement Copy trait). String is not copyable because it is a heap-allocated string and copying would need a fresh allocation (an expensive operation which better be not implicit), MyEnum1 is not copyable because it does not implement Copy (with #[deriving(Copy, Clone)], for example; and it is unclear if it can be made copyable because you didn't provide its variants), and Struct1 is not copyable because it contains non-copyable types.
In fn1 you invoke fn2, passing it field1 and getting a String back. Then you immediately passes this String to fn3. Because String is not copyable, whatever is stored in let1 is moved into the called function, making let1 inaccessible. This is what "use of moved value" error is about. (The code you provided can't cause "use of moved value: a.field1" error, so it probably came from the parts you omitted, but the basic idea is absolutely the same)
There are several ways to fix these errors, but the most natural and common one is indeed to use borrowed references. In general if you only want to read some non-copyable value in a function you should pass it there by reference:
fn use_myenum(e: &MyEnum1)
For strings and arrays, however, the better way would be to pass slices:
fn use_str(s: &str) { ... }
let s: String = ...;
use_str(&s); // here &String is automatically converted to &str
You can find more on slices in the book, here.

Resources