get file information from DirEntry in a for loop - rust

I am new to Rust. I am trying to build a JSON object where the keys are file names and the value is the file contents.
So far, I have:
use std::fs;
use std::io;
use std::env;
use std::collections::HashMap;
use std::path::{Path, PathBuf};
fn main() {
make_json();
}
fn make_json() -> io::Result<()> {
let mut modules = HashMap::new();
let mut dir = env::current_dir().unwrap();
let mut read_dir = fs::read_dir(dir);
for entry in try!(read_dir) {
let entry = try!(entry);
let file_name = entry.path().file_name().unwrap().to_string_lossy();
modules.insert(file_name, "");
}
Ok(())
}
When I go to compile it, I get
src/main.rs:19:25: 19:37 error: borrowed value does not live long enough
src/main.rs:19 let file_name = entry.path().file_name().unwrap().to_string_lossy();
^~~~~~~~~~~~
note: in expansion of for loop expansion
src/main.rs:17:5: 21:6 note: expansion site
src/main.rs:13:38: 23:2 note: reference must be valid for the block suffix following statement 0 at 13:37...
src/main.rs:13 let mut modules = HashMap::new();
src/main.rs:14 let mut dir = env::current_dir().unwrap();
src/main.rs:15 let mut read_dir = fs::read_dir(dir);
src/main.rs:16
src/main.rs:17 for entry in try!(read_dir) {
src/main.rs:18 let entry = try!(entry);
...
src/main.rs:19:9: 19:77 note: ...but borrowed value is only valid for the statement at 19:8
src/main.rs:19 let file_name = entry.path().file_name().unwrap().to_string_lossy();
^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
src/main.rs:19:9: 19:77 help: consider using a `let` binding to increase its lifetime
src/main.rs:19 let file_name = entry.path().file_name().unwrap().to_string_lossy();
^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
error: aborting due to previous error
I understand what this error is telling me; entry is defined within the scope of the for loop, and therefore if I store it to the HashMap it will no longer be valid memory because the place in memory will have been freed already. I get that.
What I don't get, is how I access the the DirEntrys within read_dir without using some sort of closure, since I will need their information outside of whatever closure I retrieve them in.
Everything that I have come across hasn't been able to help me.

DirEntry.path() returns a PathBuf, which is 'static (i.e. it contains no non-static references and is a completely standalong object). It is where the problem lies.
PathBuf.file_name() returns Option<&OsStr>, a reference into that object, and OsStr.to_string_lossy() returns Cow<str>. Note with that last that it is not 'static; with the elided lifetimes reinstated, it’s fn to_string_lossy<'a>(&'a self) -> Cow<'a, str>. This is for efficiency, because if the path is legal UTF-8 then there’s no need to go creating an entirely new owned string (String), it can keep it as a string slice (&str). (Because that’s what Cow<'a, str> is: its variants, with generics filled in, are Owned(String) and Borrowed(&'a str).)
What you need in this location is to turn the Cow<str> into a String. This is accomplished with the into_owned method of Cow<T>.
That line of code thus becomes this:
let file_name = entry.path().file_name().unwrap().to_string_lossy().into_owned();

Problem while dealing with Rust file system forced me to create this rust library brown
While dealing with Rust fs and specially while working with loops, the main issue is that every thing return another thing and then we need to convert that thing.
We need something to flatten the items for us
My suggestion :: Do not do any calculations etc in a loop, it should just have function calls to a well tested library and just checking its results.

Related

How to fix the expected enum `Result`, found reference error in Rust [duplicate]

Is there a more efficient way of getting an owned value from a HashMap than this line?
let output_items = output_tables.get(TABLE_NAME_TLIST).unwrap().to_owned();
This screenshot expands the types:
relates to Rust - Change a reference to own value without clone
If you want to take ownership of the value, HashMap::remove() will return an Option<T> rather than the Option<&T> returned by HashMap::get(). See this playground:
use std::collections::HashMap;
fn main() {
let mut map = HashMap::new();
map.insert(1, "a");
assert_eq!(map.remove(&1), Some("a"));
assert_eq!(map.remove(&1), None);
}
If you want the value to remain in the HashMap but also own the value elsewhere, you can wrap it in an Rc<T> to share ownership. If the object needs to be mutable, you can wrap it in an Rc<RefCell<T>>.

Get a Vector of Strings from a Rc<RefCell<Vec<String>>> in Rust

I have a function that takes in a Vec<String> value. I want to use this function on values contained inside my_ref, so I need to extract a Vec<String> out of a Rc<RefCell<Vec<String>>>.
I thought I could do this by dereferencing a borrow of my my_ref, just like I would for a Rc<RefCell<f32>>> or Rc<RefCell<i32>>> value:
use std::cell::RefCell;
use std::rc::Rc;
fn main() {
let my_ref = Rc::from(RefCell::from(vec![
"Hello 1".to_string(),
"Hello 2".to_string(),
]));
let my_strings: Vec<String> = *my_ref.borrow();
let count = count_strings(my_strings);
}
fn count_strings(strings: Vec<String>) -> usize {
strings.len()
}
But doing so results in a dereferencing error:
error[E0507]: cannot move out of dereference of `Ref<'_, Vec<String>>`
cannot move out of dereference of `Ref<'_, Vec<String>>`
move occurs because value has type `Vec<String>`, which does not implement the `Copy` trait
So then, how do I properly extract a Vec<String> from a Rc<RefCell<Vec<String>>>?
RefCell::borrow returns a reference, not an owned value, that's why you having such an error. I can name two different solution for that problem.
Promoting Rc to exclusively-owned type
Rc::try_unwrap is able to check, whether there's other references to the data. If it's the only one, it can be safely converted to the inner type. Then, an owned RefCell can be converted into its inner via into_inner function.
let my_ref = Rc::from(RefCell::new(vec![..]));
let inner: Vec<_> = Rc::try_unwrap(my_ref).expect("I hereby claim that my_ref is exclusively owned").into_inner();
Replacing inner value
If for some reason you want to grab inner value that is already referenced, you may consider replacing it. Note, that you need to create a appropriate value for the type (i.e. with trait Default). Here's the example:
let my_ref = Rc::from(RefCell::new(vec![..]));
let inner: Vec<_> = my_ref.borrow_mut().take();
// or
let inner: Vec<_> = my_ref.borrow_mut().replace(vec![]);

"expected struct `std::rc::Rc`, found reference" - how to convert?

I tried to get a reference-counted Rc<Foo> from a hash map and put it into a different container (Vec<Foo>).
Thought this would work (by incrementing the reference count), but instead I got an "expected struct std::rc::Rc, found reference" error.
How do I convert an &Rc<Foo> to a Rc<Foo>?
More info:
struct Foo();
let mut foo : HashMap<usize, Rc<Foo>> = HashMap::new();
let mut bar : Vec<Rc<Foo>> = Vec::new();
foo.insert(0, Rc::new(Foo()));
if let Some(x) = foo.get(&0) {
bar.push(x); // expected struct `std::rc::Rc`, found reference
// note: expected type `std::rc::Rc<Foo>`
// found type `&std::rc::Rc<Foo>` rustc(E0308)
}
I get that the hash map returns a reference to the value it owns. But dereferencing it doesn't work: both if let Some(&x) and bar.push(*x); result in a "cannot move out of borrowed content".
Curiously, adding a type annotation changes the error to "cannot move out of an Rc":
let x : &Rc<Foo> = x;
bar.push(*x); // cannot move out of an `Rc` rustc(E0507)
I need to store a reference to the same object, and not to a copy, so I avoided the .clone() "escape hatch".
To convert an &Rc<Foo> -> Rc<Foo>, use Rc::clone(), which gives you an Rc object of your own, increasing the reference count under the hood:
let ref_to_rc: &Rc<Foo> = &Rc::new(Foo());
let new_rc: Rc<Foo> = Rc::clone(ref_to_rc);
rc.clone() is equivalent to Rc::clone(&rc), but idiomatic Rust uses the latter to make it clear that the code only increases the refcount, not performing a deep copy of the data like some other implementations of .clone() do. (Though in some scenarios involving traits you might need to revert to ref_to_rc.clone().)
The errors above were about Rust refusing to do the copy implicitly. Why is std::rc::Rc<> not Copy? has an explanation of why it behaves like that.

Visibility of set outside of infinite loop filling it [duplicate]

I set myself a little task to acquire some basic Rust knowledge. The task was:
Read some key-value pairs from stdin and put them into a hashmap.
This, however, turned out to be a trickier challenge than expected. Mainly due to the understanding of lifetimes. The following code is what I currently have after a few experiments, but the compiler just doesn't stop yelling at me.
use std::io;
use std::collections::HashMap;
fn main() {
let mut input = io::stdin();
let mut lock = input.lock();
let mut lines_iter = lock.lines();
let mut map = HashMap::new();
for line in lines_iter {
let text = line.ok().unwrap();
let kv_pair: Vec<&str> = text.words().take(2).collect();
map.insert(kv_pair[0], kv_pair[1]);
}
println!("{}", map.len());
}
The compiler basically says:
`text` does not live long enough
As far as I understand, this is because the lifetime of 'text' is limited to the scope of the loop.
The key-value pair that I'm extracting within the loop is therefore also bound to the loops boundaries. Thus, inserting them to the outer map would lead to a dangling pointer since 'text' will be destroyed after each iteration. (Please tell me if I'm wrong)
The big question is: How to solve this issue?
My intuition says:
Make an "owned copy" of the key value pair and "expand" it's lifetime to the outer scope .... but I have no idea how to achieve this.
The lifetime of 'text' is limited to the scope of the loop. The key-value pair that I'm extracting within the loop is therefore also bound to the loops boundaries. Thus, inserting them to the outer map would lead to an dangling pointer since 'text' will be destroyed after each iteration.
Sounds right to me.
Make an "owned copy" of the key value pair.
An owned &str is a String:
map.insert(kv_pair[0].to_string(), kv_pair[1].to_string());
Edit
The original code is below, but I've updated the answer above to be more idiomatic
map.insert(String::from_str(kv_pair[0]), String::from_str(kv_pair[1]));
In Rust 1.1 the function words was marked as deprecated. Now you should use split_whitespace.
Here is an alternative solution which is a bit more functional and idiomatic (works with 1.3).
use std::io::{self, BufRead};
use std::collections::HashMap;
fn main() {
let stdin = io::stdin();
// iterate over all lines, "change" the lines and collect into `HashMap`
let map: HashMap<_, _> = stdin.lock().lines().filter_map(|line_res| {
// convert `Result` to `Option` and map the `Some`-value to a pair of
// `String`s
line_res.ok().map(|line| {
let kv: Vec<_> = line.split_whitespace().take(2).collect();
(kv[0].to_owned(), kv[1].to_owned())
})
}).collect();
println!("{}", map.len());
}

Storing from inside a loop a borrowed value to container in outer scope?

I set myself a little task to acquire some basic Rust knowledge. The task was:
Read some key-value pairs from stdin and put them into a hashmap.
This, however, turned out to be a trickier challenge than expected. Mainly due to the understanding of lifetimes. The following code is what I currently have after a few experiments, but the compiler just doesn't stop yelling at me.
use std::io;
use std::collections::HashMap;
fn main() {
let mut input = io::stdin();
let mut lock = input.lock();
let mut lines_iter = lock.lines();
let mut map = HashMap::new();
for line in lines_iter {
let text = line.ok().unwrap();
let kv_pair: Vec<&str> = text.words().take(2).collect();
map.insert(kv_pair[0], kv_pair[1]);
}
println!("{}", map.len());
}
The compiler basically says:
`text` does not live long enough
As far as I understand, this is because the lifetime of 'text' is limited to the scope of the loop.
The key-value pair that I'm extracting within the loop is therefore also bound to the loops boundaries. Thus, inserting them to the outer map would lead to a dangling pointer since 'text' will be destroyed after each iteration. (Please tell me if I'm wrong)
The big question is: How to solve this issue?
My intuition says:
Make an "owned copy" of the key value pair and "expand" it's lifetime to the outer scope .... but I have no idea how to achieve this.
The lifetime of 'text' is limited to the scope of the loop. The key-value pair that I'm extracting within the loop is therefore also bound to the loops boundaries. Thus, inserting them to the outer map would lead to an dangling pointer since 'text' will be destroyed after each iteration.
Sounds right to me.
Make an "owned copy" of the key value pair.
An owned &str is a String:
map.insert(kv_pair[0].to_string(), kv_pair[1].to_string());
Edit
The original code is below, but I've updated the answer above to be more idiomatic
map.insert(String::from_str(kv_pair[0]), String::from_str(kv_pair[1]));
In Rust 1.1 the function words was marked as deprecated. Now you should use split_whitespace.
Here is an alternative solution which is a bit more functional and idiomatic (works with 1.3).
use std::io::{self, BufRead};
use std::collections::HashMap;
fn main() {
let stdin = io::stdin();
// iterate over all lines, "change" the lines and collect into `HashMap`
let map: HashMap<_, _> = stdin.lock().lines().filter_map(|line_res| {
// convert `Result` to `Option` and map the `Some`-value to a pair of
// `String`s
line_res.ok().map(|line| {
let kv: Vec<_> = line.split_whitespace().take(2).collect();
(kv[0].to_owned(), kv[1].to_owned())
})
}).collect();
println!("{}", map.len());
}

Resources