I want to return String from &Option<String> which is returned from config map from configparser::ini::Ini
use configparser::ini::Ini;
fn main() {
let filename : String = "p2pvpn.conf".to_owned();
let mut config = Ini::new();
let map = config.load(filename).unwrap();
let tunc = map.get("tun").unwrap(); //returns hashmap
/*
* 1st unwrap returns &Option<String>
* 2nd unwrap should return String
*/
let tunopt : String = tunc.get("ip").unwrap().unwrap(); //here is the problem
println!("{tunopt}");
}
But I am getting this error:
--> src/main.rs:9:28
|
9 | let tunopt : String = tunc.get("ip").unwrap().unwrap();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ move occurs because value has type `Option<String>`, which does not implement the `Copy` trait
|
help: consider borrowing the `Option`'s content
|
9 | let tunopt : String = tunc.get("ip").unwrap().unwrap().as_ref();
| +++++++++
I tried that option with as_ref, but it did not helped (expected struct String, found reference), and to_string did not helped too.
I know that first unwrap after get returns &Option<String> tunc.get("ip").unwrap().unwrap().
I tried this:
let tunopt : Option<String> = *(tunc.get("ip").unwrap()); //move this, deference using asterisk
I thought it will move ownership but still not working
13 | let tunopt : Option<String> = *(tunc.get("ip").unwrap()); //move this, deference using asterisk
| ^^^^^^^^^^^^^^^^^^^^^^^^^^ move occurs because value has type `Option<String>`, which does not implement the `Copy` trait
|
help: consider borrowing the `Option`'s content
My questions are:
How to properly get value String from option reference &Option<String>?
Why I can't deference &Option<String> and move it's ownership to variable of type Option<String>?
Why is it complaining about this: not implement the 'Copy' trait', I am tring to move and not to copy.
You can't directly move a value from a HashMap using get. Moving generally imply that you are taking something from somewhere.
In this case you are trying to simply "get" the value, so you only receive the reverence to it. To move it, you can remove the element from the HashMap, so you receive the value itself.
In practice you can do is:
Copy a value from the hashMap:
let map = config.load(filename).unwrap();
let tunc = map.get("tun").unwrap();
let tunopt: &Option<String> = tunc.get("ip").unwrap();
let tunopt: &String = tunopt.as_ref().unwrap();
let tunopt: String = tunopt.to_owned();
Or take the value, AKA remove from HashMap to local var.
let mut map = config.load(filename).unwrap();
let tunc = map.get_mut("tun").unwrap();
let tunopt: Option<String> = tunc.remove("ip").unwrap();
let tunopt: String = tunopt.unwrap();
You cannot move a String out of a &Option<String>, full stop. In fact, you can't move anything out of an object behind an immutable reference (&), unless the object has some form of interior mutability (RefCell, Mutex, etc). That is the point of immutable borrows.
To move the String out of the config, the config would have to update itself to reflect that it no longer owns the string and that it should no longer attempt to access it. Otherwise, it may very well attempt to access the string after you have freed it, causing memory unsafety.
configparser::ini::Ini does have a method to remove the string value and transfer its ownership: remove_key. This will allow you to move the string out of the Ini, but as the name suggests, it does this by removing it from the Ini object.
Or you could just clone the string. Config parsing is unlikely to be a hotspot in your program, and such strings aren't usually that big.
You can't move the String out of the shared reference, but You can borrow the Option's contents by using the reference on that String:
use configparser::ini::Ini;
fn main() {
let filename: String = "p2pvpn.conf".to_owned();
let mut config = Ini::new();
let map = config.load(filename).unwrap();
let tunc = map.get("tun").unwrap(); //returns hashmap
/*
* 1st unwrap returns &Option<String>
* 2nd unwrap returns &String
*/
let tunopt: &String = tunc.get("ip").unwrap().as_ref().unwrap(); //problem solved
println!("{tunopt}");
}
Related
Rust code:
let item_discount_price = item_discount_price_element.text().collect::<String>().trim();
give error:
error[E0716]: temporary value dropped while borrowed
--> src/main.rs:64:35
|
64 | let item_discount_price = item_discount_price_element.text().collect::<String>().trim();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - temporary value is freed at the end of this statement
| |
| creates a temporary which is freed while still in use
65 | let item_discount_price = item_discount_price.trim();
| -------------------------- borrow later used here
|
= note: consider using a `let` binding to create a longer lived valu
I have solved it with following code:
let item_discount_price = item_discount_price_element.text().collect::<String>();
let item_discount_price = item_discount_price.trim();
For background I am doing some web-scraping, item_discount_price_element is ElementRef from scraper. https://docs.rs/scraper/latest/scraper/element_ref/struct.ElementRef.html
Question is why first code is not working ?
If you look at the documentation of trim() you'll notice that it's not a member of String but of str:
pub fn trim(&self) -> &str
That is, it takes a &str (string slice) and returns another &str, a subslice, with the same lifetime.
Now, your code is equivalent of doing something like:
let s: &str = String::from(" hello ").trim();
That would create a temporary String, borrow it as a &str, and compute its trim slice. Then the temporary is dropped and the trimmed slice reference is invalidated. Fortunately Rust lifetime rules prevents you from trying to use that invalid value.
If you save a temporary value in a variable you avoid dropping it, as you noticed in your code.
let s: String = String::from(" hello ");
let s: &str = s.trim();
And now the code does what you want.
If you do not need to ever use the temporary it is idiomatic in Rust to use the same name for both values, to illustrate that point, and to avoid having to think of two names (naming things is hard). Note that the first s still exists, it is not destroyed by having another variable with the same name, it is just shadowed, and it will be dropped normally at the end of its scope.
I am playing around with Rust references:
fn main() {
let str = String::from("Hallo");
let &x = &str;
}
This produces the following error:
error[E0507]: cannot move out of borrowed content
--> src/main.rs:3:9
|
3 | let &x = &str;
| ^-
| ||
| |hint: to prevent move, use `ref x` or `ref mut x`
| cannot move out of borrowed content
What is going on here?
Adding to wiomoc's answer: depending on what language(s) you've previously known, variable declaration in Rust might be a little different. Whereas in C/C++ you explicitly have to declare that you want a pointer/reference variable:
int *p = &other_int;
In Rust it's enough to just use let, so the above would in Rust be
let p = &other_int;
and when you write
let &s = &string;
It pattern-matches that, so the Rust compiler reads it roughly as "I know I have a reference, and I want to bind whatever it is referring to to the name p". If you're not familiar with pattern-matching, a more obvious example (that works in Rust as well) would be
let point = (23, 42);
let (x, y) = point;
The second line unpacks the right-hand side to match the left-hand side (both are tuples of two values) and binds the variable names on the left to the values at the same position in the structure on the right. In the case above, it's less obvious that it's matching your "structural description".
The result of let &x = &str;, i.e. "I know &str is a reference, please bind whatever it refers to to the variable x" means that you're trying to have x be the same as str, when at that line all you have is a borrowed reference to str. That's why the compiler tells you you can't create an owned value (which x would be, because it's not being created as a reference) from a reference.
You dont need that &at let x
let str = String::from("Hallo");
let x = &str;
Or if you want to declare the type manually
let string = String::from("Hallo");
let x: &str = &string;
I have a string from a CSV file that contains multiple lines:
Edit. Rust was not in the actual string.
header1,header2,header3
r1v1,r1v2,r1v3
r2v1,r2v2,r2v3
I am trying to push these into a Vec<Vec<String>>:
// the csv is raw_string
let lines = raw_string.lines();
let mut mainVec = Vec::new();
for row in lines {
let mut childVec = Vec::new();
let trimmed = row.trim();
let values = trimmed.split(',').collect();
childVec.push(values);
mainVec.push(childVec.clone());
}
But I get the error
error[E0282]: unable to infer enough type information about `_`
--> src/main.rs:9:13
|
9 | let values = trimmed.split(',').collect();
| ^^^^^^ cannot infer type for `_`
|
= note: type annotations or generic parameter binding required
Another solution, based on iterators:
fn main() {
let raw_string = r"rust
header1,header2,header3
r1v1,r1v2,r1v3
r2v1,r2v2,r2v3";
let main_vec = raw_string.lines()
.map(|s| s.trim().split(',').map(String::from).collect::<Vec<String>>())
.collect::<Vec<Vec<String>>>();
print!("{:?}", main_vec);
}
As #Simon Whitehead has already said, the the only thing you need to do with collect() is to specify the type because this is a generic method. But it may also be deduced by the compiler itself in some circumstances.
The code above is pretty verbose about type specification: actually you may leave the Vec's value type unspecified and let it be deduced by the compiler like this:
let main_vec = raw_string.lines()
.map(|s| s.trim().split(',').map(String::from).collect::<Vec<_>>())
.collect::<Vec<_>>();
For more information, see the definition of collect() method.
You need to give the compiler a little hint as to what you want values to be.
You can say you want it to be a vector of something:
let values: Vec<_> = trimmed.split(',').collect();
Working example in the playground
A few other notes about the code, if you're interested.
The variable names should be snake_case. So the vectors should be called main_vec and child_vec.
The child_vec does not need to be cloned when pushing it to the main_vec. Pushing it to main_vec transfers ownership and the loop redeclares the child_vec and so the compiler can guarantee nothing has been violated here.
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.
I am playing with Rust, and I'm trying to access the first command line argument with this code:
use std::env;
fn main() {
let args: Vec<_> = env::args().collect();
let dir = args[1];
}
And I get this error:
error[E0507]: cannot move out of indexed content
--> src/main.rs:5:15
|
5 | let dir = args[1];
| --- ^^^^^^^ cannot move out of indexed content
| |
| hint: to prevent move, use `ref dir` or `ref mut dir`
Or in later versions of Rust:
error[E0507]: cannot move out of index of `std::vec::Vec<std::string::String>`
--> src/main.rs:5:15
|
5 | let dir = args[1];
| ^^^^^^^
| |
| move occurs because value has type `std::string::String`, which does not implement the `Copy` trait
| help: consider borrowing here: `&args[1]`
If I change it to let ref dir, it compiles, but I don't grok what's going on. Could someone explain what "indexed content" means?
When you use an index operator ([]) you get the actual object at index location. You do not get a reference, pointer or copy. Since you try to bind that object with a let binding, Rust immediately tries to move (or copy, if the Copy trait is implemented).
In your example, env::args() is an iterator of Strings which is then collected into a Vec<String>. This is an owned vector of owned strings, and owned strings are not automatically copyable.
You can use a let ref binding, but the more idiomatic alternative is to take a reference to the indexed object (note the & symbol):
use std::env;
fn main() {
let args: Vec<_> = env::args().collect();
let ref dir = &args[1];
// ^
}
Implicitly moving out of a Vec is not allowed as it would leave it in an invalid state — one element is moved out, the others are not. If you have a mutable Vec, you can use a method like Vec::remove to take a single value out:
use std::env;
fn main() {
let mut args: Vec<_> = env::args().collect();
let dir = args.remove(1);
}
See also:
What is the return type of the indexing operation?
For your particular problem, you can also just use Iterator::nth:
use std::env;
fn main() {
let dir = env::args().nth(1).expect("Missing argument");
}
The accepted answer has already given the solution. I would like to explain this problem on a semantic level as a complement.
The rule is: A borrowed value can't be moved out. See this: E0507
[] operator came from the Index trait, whose function signature is:
fn index(&self, index: I) -> &<Vec<T, A> as Index<I>>::Output
As you can see, it return a reference, not own the value. Moving it out break the rule mentioned above.