fn callback_test() {
let image = HtmlImageElement::new().unwrap();
let callback: Closure<dyn Fn()> = {
Closure::wrap(Box::new(|| {
image.set_src("foo");
}))
};
image.set_onload(Some(&callback.as_ref().unchecked_ref()));
}
Here is a exemple of what I'm trying to achieve. If I don't use the move keyword before the closure declaration I get a lifetime error, and if I use it I can't assign my callback later in the function. What is the correct way to resolve this issue?
You will have to .clone() it.
The Closure only works with functions that are 'static, meaning they can't hold references to local variables. If it were to allow that, then calling that closure after callback_test() completes would try to use a dangling reference since image has already been dropped.
So move-ing it into the closure is the right move. And since you have to use it again after creating the closure, you will need two copies to work with.
Try this out:
fn callback_test() {
let image = HtmlImageElement::new().unwrap();
let callback: Closure<dyn Fn()> = {
let image = image.clone();
Closure::wrap(Box::new(move || {
image.set_src("foo");
}))
};
image.set_onload(Some(&callback.as_ref().unchecked_ref()));
}
I want to have a data-structure that allows me to have arbitrary nested HashMap. For that I've constructed the following struct:
struct Database {
children: HashMap<String, Database>,
data: String,
}
For inserting in this structure i get a list of keys and a value to insert. So for example for the input
let subkeys = vec!["key1", "key1.1", "key1.1.3"];
let value = "myvalue";
I want the database to have this (pseudo) structure:
{
"data" : "",
"children": {
"key1": {
"data" : "",
"children": {
"key1.1": {
"data" : "",
"children" : {
"key1.1.3": {
"data": "myvalue",
"children" : {}
}
}
}
}
}
}
}
and then for example for a second insert request
let subkeys = vec!["key1", "key1.1", "key1.1.2"];
let value = "myvalue2";
the structure should look (pseudo) like this:
{
"data" : "",
"children": {
"key1": {
"data" : "",
"children": {
"key1.1": {
"data" : "",
"children" : {
"key1.1.3": {
"data": "myvalue",
"children" : {}
},
"key1.1.2": {
"data": "myvalue2",
"children" : {}
}
}
}
}
}
}
}
So here is a minimal reproducible example of what I've tried (not working)
playground
use std::collections::HashMap;
struct Database {
children: HashMap<String, Database>,
data: String,
}
fn main()
{
// make a databse object
let mut db = Database {
children: HashMap::new(),
data: "root".to_string(),
};
// some example subkeys
let subkeys = vec!["key1", "key1.1", "key1.1.3"];
// and the value i want to insert
let value = "myvalue";
// a reference to the current HashMap
// initialize with the root
let mut root = &db.children;
// iterate throught subkeys
for subkey in subkeys.iter() {
// match the result of a get request to the hashmap
match root.get::<String>(&subkey.to_string()) {
Some(child) => {
// if the key already exists set the root to the children of the child
root = &child.children;
}
None => {
// if key doesnt exist add it with a ne empty hashmap
let d = Database{children: HashMap::new(), data: "".to_string()};
// set root to this new databse obejct
root = &d.children;
root.insert(subkey.to_string(), d);
}
}
}
}
So as I understand it there are to problems with this code:
&d.children get s dropped after the match and so root "kind of" has no value
also the root.insert seems to be a problem because root is a & reference, so the data it refers to cannot be borrowed as mutable`
What do I need to do to make my code work and produce results like shown above. Do I maybe need to change something in my struct Database?
First, some comments on what you have so far and why it doesn't work. root needs to be a mutable reference. Note the distinction between a mutable variable (let mut root = &db.children;) and a mutable reference (let root = &mut db.children;). The former allows the variable itself to be changed. The latter allows the data behind the reference to be changed. In this instance, we need both (let mut root = &mut db.children) because we not only change root as we iterate through the nodes, but we also modify the data behind the reference whenever we need to insert a new node.
The same thing applies to d in the inner loop (it needs to be a mutable variable), though as we'll see, mutating d isn't really what we want.
// if key doesnt exist add it with a ne empty hashmap
let d = Database{children: HashMap::new(), data: "".to_string()};
// set root to this new databse obejct
root = &mut d.children;
root.insert(subkey.to_string(), d);
Ignoring the errors for a moment, what should this code do? d is a new Database with no real data in it. Then, we set root to be the (empty) set of children of this new Database. Finally, we insert the new Database into root. But since we changed root in the second step, it's no longer the parent: we're inserting d as a child of itself!
We instead want to switch the order of the second two steps. But if we simply switch those two lines, we get the error
error[E0382]: borrow of moved value: `d`
--> src/main.rs:41:24
|
36 | let mut d = Database{children: HashMap::new(), data: "".to_string()};
| ----- move occurs because `d` has type `Database`, which does not implement the `Copy` trait
37 |
38 | root.insert(subkey.to_string(), d);
| - value moved here
...
41 | root = &mut d.children;
| ^^^^^^^^^^^^^^^ value borrowed here after move
So the problem is that d is no longer a local variable when we try to set root to its children. We need root to be the children of the just-inserted value. The usual idiom for this kind of thing is the entry API. It allows us to attempt to get a value from a HashMap and if it's not found, insert something. Most relevantly, this insertion returns a mutable reference to whatever value now resides at that key.
Now that section looks like
// if key doesnt exist add it with a new empty hashmap
let d = Database{children: HashMap::new(), data: "".to_string()};
// insert the new database object and
// set root to the hashmap of children
root = &mut root.entry(subkey.to_string()).or_insert(d).children;
At this point, we have an apparently working program. By adding a #[derive(Debug)] to Database, we can see what the database looks like with println!("{:#?}, db);. However, if we try to add in the second value, everything blows up. Rather than placing the two values side-by-side, they end up in completely separate branches of the database. This traces back to the commented out lines in the Some(child) branch of the match statement.
We'd like to set root to a mutable reference to child.children, but even just uncommenting that line without any changes causes the error that root is mutably borrow while borrowed elsewhere. The problem is that we're using the borrow in root.get(&subkey.to_string()) now. Before, since we ignored child and the other branch didn't use any data from that borrow, the borrow could end right away. Now it has to last for the whole duration of the match. This prevents us from borrowing mutably even in the None case.
Fortunately, since we're using the entry API, we don't need this match statement at all! The whole thing can just be replaced with
let d = Database {
children: HashMap::new(),
data: "".to_string(),
};
// insert the new database object and
// set root to the hashmap of children
root = &mut root.entry(subkey.to_string()).or_insert(d).children;
If the subkey already exists in the set of children, root.entry(...).or_insert(...) will point to that already existing child.
Now we just need to clean up the code. Since you're using it more that once, I'd recommend factoring the act of inserting a path of keys into a function. Rather than following the HashMap<String, Database> through the path, I'd recommend following the Database itself, since that will allow you to modify its data field at the end. To that end, I'd suggest a function with this signature:
impl Database {
fn insert_path(&mut self, path: &[&str]) -> &mut Database {
todo!()
}
}
Next, since we only need to create a new Database (d) when one doesn't already exist, we can use Entry's or_insert_with method to create the new database only when necessary. This is easiest when there's a function to create the new database, so let's add #[derive(Default)] to the list of derives on Database. That makes our function
impl Database {
fn insert_path(&mut self, path: &[&str]) -> &mut Self {
let mut root = self;
// iterate throught path
for subkey in path.iter() {
// insert the new database object if necessary and
// set root to the hashmap of children
root = root
.children
.entry(subkey.to_string())
// insert (if necessary) using the Database::default method
.or_insert_with(Database::default);
}
root
}
}
At this point we should run cargo clippy to see if there are any suggestions. There's one about using to_string on &&str. To fix that, you have two choices. One, use one of the other methods for converting &strs to Strings instead of to_string. Two, dereference the &&str before using to_string. This second option is simpler. Since we're iterating over &[&str] (Vec<&str>::iter in your original), the items in the iteration are &&str. The idiomatic way to strip off the extra layer of references is to use a pattern to destructure the items.
for &subkey in path {
^^^ this is new
... // subkey has type &str instead of &&str here
}
My last piece of advice would be to change the name of root to something more generic, like node. It's only the root right at the start, so the name is misleading after that. Here's the final code together with your tests (playground):
use std::collections::HashMap;
#[derive(Default, Debug)]
struct Database {
children: HashMap<String, Database>,
data: String,
}
impl Database {
fn insert_path(&mut self, path: &[&str]) -> &mut Self {
// node is a mutable reference to the current database
let mut node = self;
// iterate through the path
for &subkey in path.iter() {
// insert the new database object if necessary and
// set node to (a mutable reference to) the child node
node = node
.children
.entry(subkey.to_string())
.or_insert_with(Database::default);
}
node
}
}
fn main() {
// make a databse object
let mut db = Database {
children: HashMap::new(),
data: "root".to_string(),
};
// some example subkeys
let subkeys = vec!["key1", "key1.1", "key1.1.3"];
// and the value i want to insert
let value = "myvalue";
let node = db.insert_path(&subkeys);
node.data = value.to_string();
println!("{:#?}", db);
let subkeys = vec!["key1", "key1.1", "key1.1.2"];
let value = "myvalue2";
let node = db.insert_path(&subkeys);
node.data = value.to_string();
println!("{:#?}", db);
}
Given the following code (which does not compile):
fn main() {
let mut v = vec!();
{
let name = "Bob the Builder".to_string();
v.push(&name);
}
for m in &v{
println!("{}", m);
}
}
I have created a variable binding to a Rust String type which will go out of scope within the first set of curly braces. Is there a way to somehow move the ownership of the String such that the vector itself owns it?
This is an arbitrary example however I'm just trying to understand if this concept is possible.
I already know that if I use a string literal this will be regarded as a static string which will exist for the lifetime of the entire app and therefore this code would compile but I'm just trying to understand if a collection in Rust can own data. I know Rust is not Objective-C but Objective-C has collections with the ability to retain their data.
The vector will own it.. as long as you don't pass a reference to it.
Changing your code to this:
fn main() {
let mut v = vec!();
{
let name = "Bob the Builder".to_string();
v.push(name); // <--- no ampersand
println!("{}", name); // <---- error, use of moved value
}
for m in &v {
println!("{}", m);
}
}
..throws an error because name is now owned by the Vector. If you allow for the fact that the Vector now owns the string.. your code compiles (by removing my println! call):
fn main() {
let mut v = vec!();
{
let name = "Bob the Builder".to_string();
v.push(name); // <--- no ampersand
}
for m in &v {
println!("{}", m); // <--- works fine
}
}
So your problem is that you're passing a reference to your string into the vector. Essentially, at the end of the block your name value will be dropped and your &name reference in the Vector could potentially point to invalid memory.. making v[0].something_here() potentially dangerous. So the compiler stops you. But, if you transfer ownership of the name variable into the vector (by not passing a reference.. but passing the whole thing) then Rust knows to clean the string up when it cleans the Vector up.
Consider the following code example, I have a vector of JoinHandlers in which I need it iterate over to join back to the main thread, however, upon doing so I am getting the error error: cannot move out of borrowed content.
let threads = Arc::new(Mutex::new(Vec::new()));
for _x in 0..100 {
let handle = thread::spawn(move || {
//do some work
}
threads.lock().unwrap().push((handle));
}
for t in threads.lock().unwrap().iter() {
t.join();
}
Unfortunately, you can't do this directly. When Mutex consumes the data structure you fed to it, you can't get it back by value again. You can only get &mut reference to it, which won't allow moving out of it. So even into_iter() won't work - it needs self argument which it can't get from MutexGuard.
There is a workaround, however. You can use Arc<Mutex<Option<Vec<_>>>> instead of Arc<Mutex<Vec<_>>> and then just take() the value out of the mutex:
for t in threads.lock().unwrap().take().unwrap().into_iter() {
}
Then into_iter() will work just fine as the value is moved into the calling thread.
Of course, you will need to construct the vector and push to it appropriately:
let threads = Arc::new(Mutex::new(Some(Vec::new())));
...
threads.lock().unwrap().as_mut().unwrap().push(handle);
However, the best way is to just drop the Arc<Mutex<..>> layer altogether (of course, if this value is not used from other threads).
As referenced in How to take ownership of T from Arc<Mutex<T>>? this is now possible to do without any trickery in Rust using Arc::try_unwrap and Mutex.into_inner()
let threads = Arc::new(Mutex::new(Vec::new()));
for _x in 0..100 {
let handle = thread::spawn(move || {
println!("{}", _x);
});
threads.lock().unwrap().push(handle);
}
let threads_unwrapped: Vec<JoinHandle<_>> = Arc::try_unwrap(threads).unwrap().into_inner().unwrap();
for t in threads_unwrapped.into_iter() {
t.join().unwrap();
}
Play around with it in this playground to verify.
https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=9d5635e7f778bc744d1fb855b92db178
while the drain is a good solution, you can also do the following thing
// with a copy
let built_words: Arc<Mutex<Vec<String>>> = Arc::new(Mutex::new(vec![]));
let result: Vec<String> = built_words.lock().unwrap().clone();
// using drain
let mut locked_result = built_words.lock().unwrap();
let mut result: Vec<String> = vec![];
result.extend(locked_result.drain(..));
I would prefer to clone the data to get the original value. Not sure if it has any performance overhead.
I've implemented a simple linked list as a struct thusly
struct List {
data : String,
cons : Option<Box<List>>
}
I have another struct which has a member of this type, defined below
pub struct Context {
head : Option<Box<List>>
}
In a function of this struct, run, I have this code
let mut temp_head = &mut self.head;
let mut full_msg = "".to_string();
while temp_head.is_some() {
let temp_node = temp_head.unwrap();
full_msg.push_str(temp_node.data.as_slice());
temp_head = temp_node.cons;
}
To iterate through the linked list and assemble a string of their data. However, the line which sets the value of temp_node produces the following error: cannot move out of dereference of &mut-pointer, and the compiler also complains that the value I'm trying to put into temp_head at the end doesn't live past the block.
I've tried cloning temp_head on the first line or temp_node.cons on the last line to get versions with the lifespan that I want, but that just produces additional errors, and the real problem seems to be that I just don't understand why the first version doesn't work. Can someone explain what I'm doing wrong, and/or link me to the Rust docs which explain this?
You need to be very careful with references in your code, the problem is that first you indeed attempt to move the content of temp_head out of it's container, when using unwrap(). This content being moved would be destroyed at the end of the while block, leaving temp_head referring to deleted content.
You need to use references all the way, and for this pattern-matching is more appropriate than using unwrap() and is_some(), like this :
let mut temp_head = &self.head;
let mut full_msg = "".to_string();
while match temp_head {
&Some(ref temp_node) => { // get a reference to the content of node
full_msg.push_str(temp_node.data.as_slice()); // copy string content
temp_head = &temp_node.cons; // update reference
true // continue looping
},
&None => false // we reached the end, stop looping
} { /* body of while, nothing to do */ }