In my efforts to learn Rusts' notorious borrow checker I'm hopelessly stuck. I've constructed a tree structure that loosely represents a file system. As soon as I'm trying to borrow a enum value and try to mutate the underlying value, I get an error that I don't understand. Pretend that cache_files actually has some elements in it. Here's the code:
enum UnixFile{
FILE(UFile),
FOLDER(UFolder)
}
struct UFile {
name: String,
parent: Weak<RefCell<UnixFile>>,
size: usize,
}
struct UFolder {
name: String,
parent: Weak<RefCell<UnixFile>>,
files: Vec<Rc<RefCell<UnixFile>>>,
}
fn main() {
let root = Rc::new(RefCell::new(
UnixFile::FOLDER(
UFolder {
name: String::from("root"),
parent: Weak::new(),
files: vec![],
})));
let mut current = root.clone();
let mut f = &*(*current).borrow_mut();
let mut cache_files:Vec<Rc<RefCell<UnixFile>>> = Vec::new();
match f {
UnixFile::FOLDER(mut folder) => {
folder.files.append(&mut cache_files);
},
UnixFile::FILE(file) => {}
}
}
And this is the resulting error:
error[E0507]: cannot move out of `f` as enum variant `FOLDER` which is behind a shared reference
--> src/main.rs:43:11
|
43 | match f {
| ^
44 | UnixFile::FOLDER(mut folder) => {
| ----------
| |
| data moved here
| move occurs because `folder` has type `UFolder`, which does not implement the `Copy` trait
So what does this error mean? And how can I put the code in a position that I actually can add a file from cache_files to the folder?
First, you do &* for f. This gives you a shared reference. You cannot mutate it.
Instead, do &mut *. You can also simplify the expression further, removing the parentheses and asterisk thanks to auto-deref:
let mut f = &mut *current.borrow_mut();
The second problem is more subtle. When you specify UnixFolder::FOLDER(mut folder) in the match arm, the mut forces the compiler to move out of folder instead of just binding it to a reference. The explanation of why is complicated and not really relevant (what happens is that it opts out for match ergonomics, but you don't need to understand that). What you need to know is that you can just remove the mut, and then folder will have type &mut UFolder, and everything will go fine.
You are moving a value(enum) that is owned by something else. Either you implement Clone/Copy or wrap it with Rc(Arc in multhead), so you can clone the reference count and not the value.
Related
i am trying to make a simple tauri Program where i have a Filetree of all Files and sub files of a chosen directory.
I have not got many experience in Rust and ran into a problem with Ownerships.
Code
use std::fs;
use std::path::PathBuf;
use trees::Tree;
//FileTree
//https://docs.rs/crate/file_tree/0.1.1
fn main() {
let path = PathBuf::from("MyPath...");
let tree = Tree::new(path);
build_tree(tree);
}
fn build_tree( mut tree:Tree<PathBuf>){
let path = tree.front().unwrap().data();
for entry in fs::read_dir(path).unwrap() {
let entry = entry.unwrap();
let subpath = entry.path();
if subpath.is_dir() {
let subtree = Tree::new(subpath);
build_tree(subtree);
tree.push_back(subtree);
} else {
tree.push_back(Tree::new(subpath));
}
}
tree;
}
This is what i figured out so far but in the line:
tree.push_back(subtree);
I got following Error
error[E0382]: use of moved value: subtree
--> src\main.rs:23:28
|
21 | let subtree = Tree::new(subpath);
| ------- move occurs because `subtree` has type `trees::Tree<PathBuf>`, which does not implement the `Copy` trait
22 | build_tree(subtree);
| ------- value moved here
23 | tree.push_back(subtree);
| ^^^^^^^ value used here after move
What i tried
I tried to make subtree mutable but it didn't work either because it says mut is not necessary
Also i already tried to use the Filetree crate of Rust but it was not exectely what i was looking for.
Someone got other Ideas how to Implement a Filetree in Rust or Tauri?
You are trying to mutate a tree in your build_tree function, but since the function is taking ownership of whatever you pass in, you get the value used here after move error afterwards. To allow a function to mutate a variable and the variable still exist afterwards, you should instead pass in a mutable reference to the tree:
use std::fs;
use std::path::PathBuf;
use trees::Tree;
fn main() {
let path = PathBuf::from("MyPath...");
let mut tree = Tree::new(path);
build_tree(&mut tree);
}
fn build_tree(tree: &mut Tree<PathBuf>) {
let path = tree.front().unwrap().data();
for entry in fs::read_dir(path).unwrap() {
let entry = entry.unwrap();
let subpath = entry.path();
if subpath.is_dir() {
let mut subtree = Tree::new(subpath);
build_tree(&mut subtree);
tree.push_back(subtree);
} else {
tree.push_back(Tree::new(subpath));
}
}
}
Notice also that you need to change some variable declarations to use let mut so that the compiler allows a mutable reference (&mut) to that variable to be used.
This can also be done by taking ownership and returning a new value, but this method looks a little closer to what you had originally.
i have the following code snippet which implements some kind of Emitter Struct:
type Callback<'a> = Option<&'a mut dyn FnMut()>;
struct Emitter<'a> {
cb: Callback<'a>
}
impl<'a> Emitter<'a> {
fn emit(&mut self) {
if self.cb.is_some() {
let f = self.cb.unwrap();
f()
}
}
}
fn main() {
let mut cb = || println!("test");
let mut e = Emitter {
cb : Some(&mut cb)
};
e.emit();
}
The emit() function tries to run the saved callback clojure. But i cannot wrap my head around how to run the callback, since the code produces the following error:
--> src/main.rs:11:15
|
11 | let f = self.cb.unwrap();
| ^^^^^^^
| |
| move occurs because `self.cb` has type `Option<&mut dyn FnMut()>`, which does not implement the `Copy` trait
| help: consider borrowing the `Option`'s content: `self.cb.as_ref()`
Appreciate some help :)
Here is the snippet on replit: https://replit.com/#lutzer/RustEmitterTest
What's going on here is that your line
let f = self.cb.unwrap();
would want to move the closure out of the Option enum. This operation consumes that enum, which isn't allowed for things that belong to a struct.
Here is a simpler example to show what I mean:
fn main() {
let an_option = Some(String::from("Woot!");
let the_value = an_option.unwrap();
println!("The value is {}", the_value);
println!("The option is {:?}", an_option); // error here! Can't use an_option any more!!!
https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=4a4a3660b68ebada99113db5165b6e76
So if you take ownership of something stored inside the Some part of an Option, via unwrap, then the whole Option gets moved out. You can see that in the signature of unwrap:
pub const fn unwrap(self) -> T
Note how it says self, and not &self or &mut self. That means, after calling unwrap, that the enum gets consumed and cannot be used any more, unless the value inside the Some part can simply be copied (If you replace the String in my example with, say, an integer, it will compile without issue).
The comment by Omer Erden then explains a way around that: Ask the Option to give you a mutable reference instead via as_mut.
Or skip all that directly and use the map method of option, which you can use to do something if the option is Some and just not do anything if it's None.
I've very recently started studying Rust, and while working on a test program, I wrote this method:
pub fn add_transition(&mut self, start_state: u32, end_state: u32) -> Result<bool, std::io::Error> {
let mut m: Vec<Page>;
let pages: &mut Vec<Page> = match self.page_cache.get_mut(&start_state) {
Some(p) => p,
None => {
m = self.index.get_pages(start_state, &self.file)?;
&mut m
}
};
// omitted code that mutates pages
// ...
Ok(true)
}
it does work as expected, but I'm not convinced about the m variable. If I remove it, the code looks more elegant:
pub fn add_transition(&mut self, start_state: u32, end_state: u32) -> Result<bool, std::io::Error> {
let pages: &mut Vec<Page> = match self.page_cache.get_mut(&start_state) {
Some(p) => p,
None => &mut self.index.get_pages(start_state, &self.file)?
};
// omitted code that mutates pages
// ...
Ok(true)
}
but I get:
error[E0716]: temporary value dropped while borrowed
--> src\module1\mod.rs:28:29
|
26 | let pages: &mut Vec<Page> = match self.page_cache.get_mut(&start_state) {
| _____________________________________-
27 | | Some(p) => p,
28 | | None => &mut self.index.get_pages(start_state, &self.file)?
| | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^-
| | | |
| | | temporary value is freed at the end of this statement
| | creates a temporary which is freed while still in use
29 | | };
| |_________- borrow later used here
|
= note: consider using a `let` binding to create a longer lived value
I fully understand the error, which directed me to the working snippet, but I'm wondering if there's a more elegant and/or idiomatic way of writing this code. I am declaring m at the beginning of the function, only to prevent a temporary variable from being freed too early. Is there a way of telling the compiler that the lifetime of the return value of self.index.get_pages should be the whole add_transition function?
Further details:
Page is a relatively big struct, so I'd rather not implement the Copy trait nor I'd clone it.
page_cache is of type HashMap<u32, Vec<Page>>
self.index.get_pages is relatively slow and I'm using page_cache to cache results
The return type of self.index.get_pages is Result<Vec<Page>, std::io::Error>
This is normal, your 'cleaner' code basically comes down to do something as follows:
let y = {
let x = 42;
&x
};
Here it should be obvious that you cannot return a reference to x because x is dropped at the end of the block. Those rules don't change when working with temporary values: self.index.get_pages(start_state, &self.file)? creates a temporary value that is dropped at the end of the block (line 29) and thus you can't return a reference to it.
The workaround via m now moves that temporary into the m binding one block up which will live long enough for pages to work with it.
Now for alternatives, I guess page_cache is a HashMap? Then you could alternatively do something like let pages = self.page_cache.entry(start_state).or_insert_with(||self.index.get_pages(...))?;. The only problem with that approach is that get_pages returns a Result while the current cache stores Vec<Page> (the Ok branch only). You could adapt the cache to actually store Result instead, which I think is semantically also better since you want to cache the results of that function call, so why not do that for Err? But if you have a good reason to not cache Err, the approach you have should work just fine.
Yours is probably the most efficient way, but in theory not necessary, and one can be more elegant.
Another way of doing it is to use a trait object in this case — have the variable be of the type dyn DerefMut<Vec<Page>>. This basically means that this variable can hold any type that implements the trait DerefMut<Vec<Page>>>, two types that do so are &mut Vec<Page> and Vec<Page>, in that case the variable can hold either of these, but the contents can only be referenced via DerefMut.
So the following code works as an illustration:
struct Foo {
inner : Option<Vec<i32>>,
}
impl Foo {
fn new () -> Self {
Foo { inner : None }
}
fn init (&mut self) {
self.inner = Some(Vec::new())
}
fn get_mut_ref (&mut self) -> Option<&mut Vec<i32>> {
self.inner.as_mut()
}
}
fn main () {
let mut foo : Foo = Foo::new();
let mut m : Box<dyn AsMut<Vec<i32>>> = match foo.get_mut_ref() {
Some(r) => Box::new(r),
None => Box::new(vec![1,2,3]),
};
m.as_mut().as_mut().push(4);
}
The key here is the type Box<dyn AsMut<Vec<i32>>; this means that it can be a box that holds any type, so long the type implement AsMut<Vec<i32>>, because it's boxed in we also need .as_mut().as_mut() to get the actual &mut <Vec<i32>> out of it.
Because different types can have different sizes; they also cannot be allocated on the stack, so they must be behind some pointer, a Box is typically chosen therefore, and in this case necessary, a normal pointer that is sans ownership of it's pointee will face similar problems to those you face.
One might argue that this code is more elegant, but yours is certainly more efficient and does not require further heap allocation.
I'm doing Advent Of Code Day 7 in Rust. I have to parse a tree out of order like so:
a(10)
c(5) -> a, b
b(20)
That says c is the root with a and b as its children.
I handle this by parsing each line, making an object, and storing it in a hash by name. If it shows up later as a child, like a, I can use that hash to lookup the object and apply it as a child. If it shows up as a child before being defined, like b, I can create a partial version and update it via the hash. The above would be something like:
let mut np = NodeParser{
map: HashMap::new(),
root: None,
};
{
// This would be the result of parsing "a(10)".
{
let a = Node{
name: "a".to_string(),
weight: Some(10),
children: None
};
np.map.insert( a.name.clone(), a );
}
// This is the result of parsing "c(5) -> a, b".
// Note that it creates 'b' with incomplete data.
{
let b = Node{
name: "b".to_string(),
weight: None,
children: None
};
np.map.insert("b".to_string(), b);
let c = Node{
name: "c".to_string(),
weight: Some(5),
children: Some(vec![
*np.map.get("a").unwrap(),
// ^^^^^^^^^^^^^^^^^^^^^^^^^ cannot move out of borrowed content
*np.map.get("b").unwrap()
// ^^^^^^^^^^^^^^^^^^^^^^^^^ cannot move out of borrowed content
])
};
np.map.insert( c.name.clone(), c );
}
// Parsing "b(20)", it's already seen b, so it updates it.
// This also updates the entry in c.children. It avoids
// having to search all nodes for any with b as a child.
{
let mut b = np.map.get_mut( "b" ).unwrap();
b.weight = Some(20);
}
}
I might want to look up a node and look at its children.
// And if I wanted to look at the children of c...
let node = np.map.get("c").unwrap();
for child in node.children.unwrap() {
// ^^^^ cannot move out of borrowed content
println!("{:?}", child);
}
Rust does not like this. It doesn't like that both NodeParser.map and Node.children own a node.
error[E0507]: cannot move out of borrowed content
--> /Users/schwern/tmp/test.rs:46:21
|
46 | *np.map.get("a").unwrap(),
| ^^^^^^^^^^^^^^^^^^^^^^^^^ cannot move out of borrowed content
error[E0507]: cannot move out of borrowed content
--> /Users/schwern/tmp/test.rs:49:21
|
49 | *np.map.get("b").unwrap()
| ^^^^^^^^^^^^^^^^^^^^^^^^^ cannot move out of borrowed content
It doesn't like that the for loop is trying to borrow the node to iterate because I've already borrowed the node from the NodeParser that owns it.
error[E0507]: cannot move out of borrowed content
--> /Users/schwern/tmp/test.rs:68:18
|
68 | for child in node.children.unwrap() {
| ^^^^ cannot move out of borrowed content
I think I understand what I'm doing wrong, but I'm not sure how to make it right.
How should I construct this to make the borrower happy? Because of the way NodeParser.map and Node.children must be linked, copying is not an option.
Here is the code to test with. In the real code both Node and NodeParser have implementations and methods.
One option is unsafe code ... but I would suggest avoiding that if you're using the Advent of Code to learn idiomatic Rust and not just drop all the safety its trying to give you.
Another option is to reference count the Node instances so that the borrow checker is happy and the compiler knows how to clean things up. The std::rc::Rc type does this for you ... and essentially every call to clone() just increments a reference count and returns a new Rc instance. Then every time an object is dropped, the Drop implementation just decrements the reference count.
As for the iteration .. for x in y is syntactic sugar for for x in y.into_iter(). This is attempting to move the contents of children out of node (notice in the IntoIterator trait, into_iter(self) takes ownership of self). To rectify this, you can ask for a reference instead when iterating, using for x in &y. This essentially becomes for x in y.iter(), which does not move the contents.
Here are these suggestions in action.
use std::collections::HashMap;
use std::rc::Rc;
struct NodeParser {
map: HashMap<String, Rc<Node>>,
root: Option<Node>,
}
#[derive(Debug)]
struct Node {
name: String,
children: Option<Vec<Rc<Node>>>,
}
fn main() {
let mut np = NodeParser{
map: HashMap::new(),
root: None,
};
let a = Rc::new(Node{ name: "a".to_string(), children: None });
np.map.insert( a.name.clone(), a.clone() );
let b = Rc::new(Node{ name: "b".to_string(), children: None });
np.map.insert( b.name.clone(), b.clone() );
let c = Rc::new(Node{
name: "c".to_string(),
children: Some(vec![a, b])
});
np.map.insert( c.name.clone(), c.clone() );
let node = np.map.get("c").unwrap();
for child in &node.children {
println!("{:?}", child);
}
}
EDIT: I will expand on my comment here. You can use lifetimes here too if you want, but I'm concerned that the lifetime solution will work against the MCVE and won't work once applied to the actual problem the OP (not just of this question... others as well) actually has. Lifetimes are tricky in Rust and small things like re-ordering the instantiation of variables to allow the lifetime solution can throw people off. My concern being they will run into lifetime issues and therefore the answers won't be appropriate to their actual situation even if it works for the MCVE. Maybe I overthink that though..
I'm facing a problem with a value being dropped while it is still borrowed inside an Option, in a closure, but I'm having a hard time grasping exactly what's going on. To illustrate, here is a working example of what I'm actually trying to achieve:
fn foo() -> Option<String> {
let hd = match std::env::home_dir() {
Some(d) => d,
None => return None,
};
let fi = match hd.file_name() {
Some(f) => f,
None => return None,
};
let st = match fi.to_str() {
Some(s) => s,
None => return None,
};
Some(String::from(st))
}
The return value is the base name of the current user's home directory inside Option<String>.
I thought I'd try refactoring this with combinators to get rid of the lines None => return None,.
std::env::home_dir()
.and_then(|d| d.file_name())
.and_then(|f| f.to_str())
.map(String::from)
But rustc detects a reference that outlives its value.
error: `d` does not live long enough
--> src/main.rs:33:35
|
33 | .and_then(|d| d.file_name())
| - ^ `d` dropped here while still borrowed
| |
| borrow occurs here
34 | .and_then(|f| f.to_str())
35 | .map(String::from)
| - borrowed value needs to live until here
I think this is because the reference in Option<&OsStr> is outliving the value of type PathBuf. However I'm still having a hard time figuring out how to approach this without having the value go out of scope too soon.
To further illustrate what I'm trying to achieve, here is a similar example with a type that implements the Copy trait.
let x = 42u16.checked_add(1234)
.and_then(|i| i.checked_add(5678))
.and_then(|i| i.checked_sub(90))
.map(|i| i.to_string());
println!("{:?}", x); // Some("6864")
So I'm definitely overlooking a few things related to ownership in the prior example. Is this possible with Option<PathBuf>?
You're right that you're consuming the PathBuf returned from home_dir() but still trying to use references.
I would keep it in a variable, and work from there:
fn foo() -> Option<String> {
let path = std::env::home_dir();
path.as_ref()
.and_then(|d| d.file_name())
.and_then(|f| f.to_str())
.map(String::from)
}
(Playground)
The call to path.as_ref() makes an Option<&PathBuf> as the starting point for the chain of and_then, without consuming the original owned PathBuf which is needed at least until String::from.
Expanding on Chris's answer: You can also fix the issue by nesting the chain starting from the second and_then into the closure passed to the first and_then. This works because it keeps d (which owns a PathBuf) alive until the borrows on it are released.
fn foo() -> Option<String> {
std::env::home_dir().and_then(|d| {
d.file_name()
.and_then(|f| f.to_str())
.map(String::from)
})
}