How to keep using a value after pushing it into a vector? - vector

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)?);
}

Related

How to return a single element from a Vec from a function?

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
}

How can I copy a vector to another location and reuse the existing allocated memory?

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..]);
}

use of moved value, which is non-copyable [E0382] [E0277]

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.

Weird behaviour with struct constructors

I've written a basic Node struct in D, designed to be used as a part of a tree-like structure. The code is as follows:
import std.algorithm: min;
alias Number = size_t;
struct Node {
private {
Node* left, right, parent;
Number val;
}
this(Number n) {val = n;}
this(ref Node u, ref Node v) {
this.left = &u;
this.right = &v;
val = min(u.val, v.val);
u.parent = &this;
v.parent = &this;
}
}
Now, I wrote a simple function which is supposed to give me a Node (meaning a whole tree) with the argument array providing the leaves, as follows.
alias Number = size_t;
Node make_tree (Number[] nums) {
if (nums.length == 1) {
return Node(nums[0]);
} else {
Number half = nums.length/2;
return Node(make_tree(nums[0..half]), make_tree(nums[half..$]));
}
}
Now, when I try to run it through dmd, I get the following error message:
Error: constructor Node.this (ulong n) is not callable using argument types (Node, Node)
This makes no sense to me - why is it trying to call a one-argument constructor when given two arguments?
The problem has nothing to do with constructors. It has to do with passing by ref. The constructor that you're trying to use
this(ref Node u, ref Node v) {...}
accepts its arguments by ref. That means that they must be lvalues (i.e. something that can be on the left-hand side of an assignment). But you're passing it the result of a function call which does not return by ref (so, it's returning a temporary, which is an rvalue - something that can go on the right-hand side of an assignment but not the left). So, what you're trying to do is illegal. Now, the error message isn't great, since it's giving an error with regards to the first constructor rather than the second, but regardless, you don't have a constructor which matches what you're trying to do. At the moment, I can think of 3 options:
Get rid of the ref on the constructor's parameters. If you're only going to be passing it the result of a function call like you're doing now, having it accept ref doesn't help you anyway. The returned value will be moved into the function's parameter, so no copy will take place, and ref isn't buying you anything. Certainly, assigning the return values to local variables so that you can pass them to the constructor as it's currently written would lose you something, since then you'd be making unnecessary copies.
Overload the constructor so that it accepts either ref or non-ref. e.g.
void foo(ref Bar b) { ... }
void foo(Bar b) { foo(b); } //this calls the other foo
In general, this works reasonably well when you have one parameter, but it would be a bit annoying here, because you end up with an exponential explosion of function signatures as you add parameters. So, for your constructor, you'd end up with
this(ref Node u, ref Node v) {...}
this(ref Node u, Node v) { this(u, v); }
this(Node u, ref Node v) { this(u, v); }
this(Node u, Node v) { this(u, v); }
And if you added a 3rd parameter, you'd end up with eight overloads. So, it really doesn't scale beyond a single parameter.
Templatize the constructor and use auto ref. This essentially does what #2 does, but you only have to write the function once:
this()(auto ref Node u, auto ref Node v) {...}
This will then generate a copy of the function to match the arguments given (up to 4 different versions of it with the full function body in each rather than 3 of them just forwarding to the 4th one), but you only had to write it once. And in this particular case, it's probably reasonable to templatize the function, since you're dealing with a struct. If Node were a class though, it might not make sense, since templated functions can't be virtual.
So, if you really want to be able to pass by ref, then in this particular case, you should probably go with #3 and templatize the constructor and use auto ref. However, personally, I wouldn't bother. I'd just go with #1. Your usage pattern here wouldn't get anything from auto ref, since you're always passing it two rvalues, and your Node struct isn't exactly huge anyway, so while you obviously wouldn't want to copy it if you don't need to, copying an lvalue to pass it to the constructor probably wouldn't matter much unless you were doing it a lot. But again, you're only going to end up with a copy if you pass it an lvalue, since an rvalue can be moved rather than copied, and you're only passing it rvalues right now (at least with the code shown here). So, unless you're doing something different with that constructor which would involve passing it lvalues, there's no point in worrying about lvalues - or about the Nodes being copied when they're returned from a function and passed into the constructor (since that's a move, not a copy). As such, just removing the refs would be the best choice.

Newbie on use of recursion in Groovy/traverse tree?

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()
}

Resources