I have an ownership problem which I don't understand well. Basically I try to create some hardlinks on my file system and to remove them after being created. Therefore I created a range of integers which I map to the actual file names I like to create and destroy. My naive solution looks like this:
use std::fs;
const src_file: &'static str = "a.txt";
const file_ext: &'static str = ".txt";
fn create_hardlink(dest_file: &str) {
fs::hard_link(&src_file, &dest_file);
}
fn main() {
let create = (0..10000).map(|x| x.to_string() + file_ext);
let remove = (0..10000).map(|x| x.to_string() + file_ext);
for file in create {
create_hardlink(&file);
}
for file in remove {
fs::remove_file(&file);
}
}
But what I actually like to accomplish is a solution, where I don't have to repeat my self for creating the static collection with the file-names and can reuse files for a second for-loop:
...
fn main() {
let files = (0..10000).map(|x| x.to_string() + file_ext);
for file in files {
create_hardlink(&file);
}
for file in files {
fs::remove_file(&file);
}
}
So when I try this the compiler complains, that the second usage of files is not possible,
src/main.rs:20:17: 20:22 error: use of moved value: `files` [E0382]
src/main.rs:20 for file in files {
because files already moved into the first for-loop:
src/main.rs:16:17: 16:22 note: `files` moved here because it has type `core::iter::Map<core::ops::Range<i32>, [closure#src/main.rs:14:36: 14:64]>`, which is non-copyable
after reading the explanation for rustc --explain E0382 I decided to change the code as follows:
...
fn main() {
let files = Rc::new(RefCell::new((0..10000).map(|x| x.to_string() + file_ext)));
for file in files.clone() {
create_hardlink(&file);
}
for file in files.clone() {
fs::remove_file(&file);
}
}
But this does not work as expected to me:
src/main.rs:16:5: 18:6 error: the trait `core::iter::Iterator` is not implemented for the type `alloc::rc::Rc<core::cell::RefCell<core::iter::Map<core::ops::Range<_>, [closure#src/main.rs:14:53: 14:81]>>>` [E0277]
src/main.rs:16 for file in files.clone() {
src/main.rs:17 create_hardlink(&file);
src/main.rs:18 }
note: in expansion of for loop expansion
src/main.rs:16:5: 18:6 note: expansion site
src/main.rs:16:5: 18:6 help: run `rustc --explain E0277` to see a detailed explanation
src/main.rs:16:5: 18:6 note: `alloc::rc::Rc<core::cell::RefCell<core::iter::Map<core::ops::Range<_>, [closure#src/main.rs:14:53: 14:81]>>>` is not an iterator; maybe try calling `.iter()` or a similar method
src/main.rs:16 for file in files.clone() {
src/main.rs:17 create_hardlink(&file);
src/main.rs:18 }
note: in expansion of for loop expansion
src/main.rs:16:5: 18:6 note: expansion site
src/main.rs:16:5: 18:6 note: required by `core::iter::IntoIterator::into_iter`
src/main.rs:16 for file in files.clone() {
src/main.rs:17 create_hardlink(&file);
src/main.rs:18 }
What can I do? Do I really have to implement the core::iter::Iterator for the type alloc::rc::Rc<core::cell::RefCell<core::iter::Map<core::ops::Range<_> like rustc --explain E0277 is telling me? I hope not...
Is there a simple solution like defining files statically as staticor as const? Or is my approach with mapping a Range non rusty?
Why do I have a type like <core::iter::Map<core::ops::Range<_> and not something like <core::iter::String>?
I hope you can help me out with that and enlighten a bit the Rust ownership principle to a novice like me.
Rust iterators are only forward iterators, as far as I understand, so they can only be iterated once. You can either collect them into a vector or use a function to generate your iterator:
// 1st option
let files: Vec<_> = (0..10000).map(|x| x.to_string() + file_ext).collect();
for f in &files { ... } // Borrow `files`
// 2nd option
let files = || (0..10000).map(|x| x.to_string() + file_ext);
for f in files() { ... } // Call the closure to get an iterator
There are several problems here.
The first is that calling
for f in files { ... }
will take files by value. This is avoidable by taking a reference instead:
for f in &files { ... }
because (&foo).into_iter() effectively resolves to foo.iter().
The second is that files must be mut, and the reference &mut if you are iterating an iterator. If you had some vector, it would make sense to iterate &my_vector - you can iterate it without modifying it. However, if you have an iterator itself, the state is kept and updated in the iterator itself.
let mut files = (0..10000).map(|x| x.to_string() + file_ext);
for file in &mut files {
create_hardlink(&file);
}
for file in files {
fs::remove_file(&file);
}
The third is that even if you did these things, since you are using a single iterator, you can only iterate each element once! The second loop will be empty. This is the problem #filmor offers solutions for.
Related
So I want to keep using a value after pushing it into a vector, but when I add it to the vector it takes ownership control over the variable, so when I want to make reference to it again I can't. How should I approach this scenario?
fn scan_recursive(dir: &Path) -> io::Result<Vec<PathBuf>> {
let mut files = Vec::new(); // Create a mutable vector to store the files.
for entry in fs::read_dir(dir)? {
let entry = entry?;
let path = entry.path();
if path.is_file() {
files.push(path);
}
if path.is_dir() { // ERROR: path is no longer valid
files.append(&mut scan_recursive(&path)?);
}
}
Ok(files)
}
The problem is that the compiler does not know if the first if is executed and cannot tell if the path variable is consumed or not, so it assumes that it always is, and the lifetime of path ends after the push to the vector.
Given that your two ifs are mutually exclusive (the path is either a file or a directory) you can use if ... else, which will let the compiler deduce that if the path is not a file, the variables has not been consumed in the if branch and it will still be available in the else branch.
if path.is_file() {
files.push(path);
}
else if path.is_dir() {
files.append(&mut scan_recursive(&path)?);
}
I'm new to Rust, and I'm trying to make an interface where the user can choose a file by typing the filename from a list of available files.
This function is supposed to return the DirEntry corresponding to the chosen file:
fn ask_user_to_pick_file(available_files: Vec<DirEntry>) -> DirEntry {
println!("Which month would you like to sum?");
print_file_names(&available_files);
let input = read_line_from_stdin();
let chosen = available_files.iter()
.find(|dir_entry| dir_entry.file_name().into_string().unwrap() == input )
.expect("didnt match any files");
return chosen
}
However, it appears chosen is somehow borrowed here? I get the following error:
35 | return chosen
| ^^^^^^ expected struct `DirEntry`, found `&DirEntry`
Is there a way I can "unborrow" it? Or do I have to implement the Copy trait for DirEntry?
If it matters I don't care about theVec after this method, so if "unborrowing" chosen destroys the Vec, thats okay by me (as long as the compiler agrees).
Use into_iter() instead of iter() so you get owned values instead of references out of the iterator. After that change the code will compile and work as expected:
fn ask_user_to_pick_file(available_files: Vec<DirEntry>) -> DirEntry {
println!("Which month would you like to sum?");
print_file_names(&available_files);
let input = read_line_from_stdin();
let chosen = available_files
.into_iter() // changed from iter() to into_iter() here
.find(|dir_entry| dir_entry.file_name().into_string().unwrap() == input)
.expect("didnt match any files");
chosen
}
In C++, to copy the contents of a vector to another vector we use the assignment operator dest = src. However, in Rust src would be moved into dest and no longer usable.
I know the simplest answer is to do dest = src.clone() (for the sake of this question we'll assume T in Vec<T> is Clone). However - if I'm understanding correctly - this creates a brand new third vector with the copied contents of src and moves it into dest, throwing away dest's dynamically allocated array. If this is correct, it's a completely unnecessary dynamic allocation when we could have just copied the content directly into dest (assuming it had sufficient capacity).
Below is a function I've made that does exactly what I would like to do: empty out the dest vector and copy the elements of src to it.
// copy contents of src to dest without just cloning src
fn copy_content<T: Clone>(dest: &mut Vec<T>, src: &Vec<T>) {
dest.clear();
if dest.capacity() < src.len() {
dest.reserve(src.len());
}
for x in src {
dest.push(x.clone());
}
}
Is there a way to do this with builtin or standard library utilities? Is the dest = src.clone() optimized by the compiler to do this anyway?
I know that if T has dynamic resources then the extra allocation from src.clone() isn't a big deal, but if T is e.g. i32 or any other Copy type then it forces an allocation where none are necessary.
Did you ever look at the definition of Clone? It has the well known clone method but also a useful but often forgotten clone_from method:
pub trait Clone : Sized {
fn clone(&self) -> Self;
fn clone_from(&mut self, source: &Self) {
*self = source.clone()
}
}
To quote the doc:
Performs copy-assignment from source.
a.clone_from(&b) is equivalent to a = b.clone() in functionality, but can be overridden to reuse the resources of a to avoid unnecessary allocations.
Of course a type such as Vec does not use the provided-by-default clone_from and defines its own in a more efficient way, similar to what you would get in C++ from writing dest = src:
fn clone_from(&mut self, other: &Vec<T>) {
other.as_slice().clone_into(self);
}
with [T]::clone_into being defined as:
fn clone_into(&self, target: &mut Vec<T>) {
// drop anything in target that will not be overwritten
target.truncate(self.len());
let len = target.len();
// reuse the contained values' allocations/resources.
target.clone_from_slice(&self[..len]);
// target.len <= self.len due to the truncate above, so the
// slice here is always in-bounds.
target.extend_from_slice(&self[len..]);
}
I have a program that more or less looks like this
struct Test<T> {
vec: Vec<T>
}
impl<T> Test<T> {
fn get_first(&self) -> &T {
&self.vec[0]
}
fn do_something_with_x(&self, x: T) {
// Irrelevant
}
}
fn main() {
let t = Test { vec: vec![1i32, 2, 3] };
let x = t.get_first();
t.do_something_with_x(*x);
}
Basically, we call a method on the struct Test that borrows some value. Then we call another method on the same struct, passing the previously obtained value.
This example works perfectly fine. Now, when we make the content of main generic, it doesn't work anymore.
fn generic_main<T>(t: Test<T>) {
let x = t.get_first();
t.do_something_with_x(*x);
}
Then I get the following error:
error: cannot move out of borrowed content
src/main.rs:14 let raw_x = *x;
I'm not completely sure why this is happening. Can someone explain to me why Test<i32> isn't borrowed when calling get_first while Test<T> is?
The short answer is that i32 implements the Copy trait, but T does not. If you use fn generic_main<T: Copy>(t: Test<T>), then your immediate problem is fixed.
The longer answer is that Copy is a special trait which means values can be copied by simply copying bits. Types like i32 implement Copy. Types like String do not implement Copy because, for example, it requires a heap allocation. If you copied a String just by copying bits, you'd end up with two String values pointing to the same chunk of memory. That would not be good (it's unsafe!).
Therefore, giving your T a Copy bound is quite restrictive. A less restrictive bound would be T: Clone. The Clone trait is similar to Copy (in that it copies values), but it's usually done by more than just "copying bits." For example, the String type will implement Clone by creating a new heap allocation for the underlying memory.
This requires you to change how your generic_main is written:
fn generic_main<T: Clone>(t: Test<T>) {
let x = t.get_first();
t.do_something_with_x(x.clone());
}
Alternatively, if you don't want to have either the Clone or Copy bounds, then you could change your do_something_with_x method to take a reference to T rather than an owned T:
impl<T> Test<T> {
// other methods elided
fn do_something_with_x(&self, x: &T) {
// Irrelevant
}
}
And your generic_main stays mostly the same, except you don't dereference x:
fn generic_main<T>(t: Test<T>) {
let x = t.get_first();
t.do_something_with_x(x);
}
You can read more about Copy in the docs. There are some nice examples, including how to implement Copy for your own types.
In our current application we have a need to traverse down a tree and capture all operators on a specific device (and child devices). A device could have child devices with also specific operators on it.
As i am new to the use of recursion in Groovy i am wondering if i am doing things right..?
Any pointer to help me learn better ways of doing things?
def listOperators(device) {
// list with all operator id's
def results = []
// closure to traverse down the tree
def getAllOperators = { aDevice->
if(aDevice) {
aDevice.operators.each { it ->
results << it.id
}
}
if (aDevice?.children) {
aDevice.children.each { child ->
results << owner.call(child)
}
}
}
// call the closure with the given device
getAllOperators(device)
// return list with unique results
return results.unique()
}
A couple things to note:
Doing the recursive call through owner is not a good idea. The definition of owner changes if the call is nested within another closure. It's error prone and has no advantages over just using the name. When the closure is a local variable, split its up the declaration and definition of the closure so the name is in scope. E.g.:
def getAllOperators
getAllOperators = { ...
You are appending the operators to a result list outside the recursive closure. But you are also appending the result of each recursive call to the same list. Either append to the list or store the results from each recursive call, but not both.
Here's a simpler alternative:
def listOperators(device) {
def results = []
if (device) {
results += device.operators*.id
device.children?.each { child ->
results += listOperators(child)
}
}
results.unique()
}