I this tiny program, but I can't make it run. I get type mismatches between &str and String or similar errors.
So this is the program
use std::fs::File;
use std::io;
use std::io::prelude::*;
use std::io::BufReader;
use std::collections::HashMap;
fn main() {
let mut f = File::open("/home/asti/class.csv").expect("Couldn't open file");
let mut s = String::new();
let reader = BufReader::new(f);
let lines: Result<Vec<_>,_> = reader.lines().collect();
let mut class_students: HashMap<String, Vec<String>> = HashMap::new();
for l in lines.unwrap() {
let mut str_vec: Vec<&str> = l.split(";").collect();
println!("{}", str_vec[2]);
let e = class_students.entry(str_vec[2]).or_insert(vec![]);
e.push(str_vec[2]);
}
println!("{}", class_students);
}
I constantly get this error:
hello_world.rs:20:38: 20:48 error: mismatched types:
expected `collections::string::String`,
found `&str`
(expected struct `collections::string::String`,
found &-ptr) [E0308]
hello_world.rs:20 let e = class_students.entry(str_vec[2]).or_insert(vec![]);
^~~~~~~~~~
I tried changing the line
let mut str_vec: Vec<&str> = l.split(";").collect();
to
let mut str_vec: Vec<String> = l.split(";").collect();
But I got this error:
hello_world.rs:16:53: 16:60 error: the trait `core::iter::FromIterator<&str>` is not implemented for the type `collections::vec::Vec<collections::string::String>` [E0277]
hello_world.rs:16 let mut str_vec: Vec<String> = l.split(";").collect();
So how do I either extract String from l instead of &str? Also, if there's a better solution let me know please as my newbiness with this technology is probably apparent to all.
A more detailed answer than a comment:
The reason your example fails to compile initially is because you are trying to insert a slice into a vector of Strings. Because the primitive type str implements the ToString trait, you can call the to_string() method to convert it to a String giving your vector the correct type.
Another option would be to_owned() as illustrated in this thread.
Related
I'm working on a .obj reader in Rust, and I've run into a bit of an issue. The only way I've found you are able to read a file is through "BufReader", though I can't seem to be able to convert that to a String for further processing. Here's my code so far:
let input: BufReader<File> = BufReader::new(File::open("cube.obj").expect("didn't work"));
let model: Obj = Obj::load(input);
let s: String = input.chars()
.map(|x| x.ok().unwrap())
.take_while(|&x| x != ' ')
.collect();
println!("input: {}", input);
If anybody more experienced with rust knows what I could do, it would be very helpful. Thanks!
You have to import the trait std::io::Read and then can call Read::read_to_string to read the content of the file.
use std::fs::File;
use std::io::{BufReader, Read};
fn main() {
let mut input: BufReader<File> = BufReader::new(File::open("cube.obj").expect("didn't work"));
let mut str = String::new();
input.read_to_string(&mut str).expect("cannot read string");
println!("input: {}", str);
}
I'm trying to read a file into a vector, then print out a random line from that vector.
What am I doing wrong?
I'm asking here because I know I'm making a big conceptual mistake, but I'm having trouble identifying exactly where it is.
I know the error -
error[E0308]: mismatched types
26 | processor(&lines)
| ^^^^^^ expected &str, found struct std::string::String
And I see that there's a mismatch - but I don't know how to give the right type, or refactor the code for that (very short) function.
My code is below:
use std::{
fs::File,
io::{prelude::*, BufReader},
path::Path,
};
fn lines_from_file(filename: impl AsRef<Path>) -> Vec<String> {
let file = File::open(filename).expect("no such file");
let buf = BufReader::new(file);
buf.lines()
.map(|l| l.expect("Could not parse line"))
.collect()
}
fn processor(vectr: &Vec<&str>) -> () {
let vec = vectr;
let index = (rand::random::<f32>() * vec.len() as f32).floor() as usize;
println!("{}", vectr[index]);
}
fn main() {
let lines = lines_from_file("./example.txt");
for line in lines {
println!("{:?}", line);
}
processor(&lines);
}
While you're calling the processor function you're trying to pass a Vec<String> which is what the lines_from_file returns but the processor is expecting a &Vec<&str>. You can change the processor to match that expectation:
fn processor(vectr: &Vec<String>) -> () {
let vec = vectr;
let index = (rand::random::<f32>() * vec.len() as f32).floor() as usize;
println!("{}", vectr[index]);
}
The main function:
fn main() {
let lines = lines_from_file("./example.txt");
for line in &lines {. // &lines to avoid moving the variable
println!("{:?}", line);
}
processor(&lines);
}
More generally, a String is not the same as a string slice &str, therefore Vec<String> is not the same as Vec<&str>. I'd recommend checking the rust book: https://doc.rust-lang.org/nightly/book/ch04-03-slices.html?highlight=String#string-slices
Suppose I have a HashMap and I want to get a mutable reference to an entry, or if that entry does not exist I want a mutable reference to a new object, how can I do it? I've tried using unwrap_or(), something like this:
fn foo() {
let mut map: HashMap<&str, Vec<&str>> = HashMap::new();
let mut ref = map.get_mut("whatever").unwrap_or( &mut Vec::<&str>::new() );
// Modify ref.
}
But that doesn't work because the lifetime of the Vec isn't long enough. Is there any way to tell Rust that I want the returned Vec to have the same lifetime as foo()? I mean there is this obvious solution but I feel like there should be a better way:
fn foo() {
let mut map: HashMap<&str, Vec<&str>> = HashMap::new();
let mut dummy: Vec<&str> = Vec::new();
let mut ref = map.get_mut("whatever").unwrap_or( &dummy );
// Modify ref.
}
As mentioned by Shepmaster, here is an example of using the entry pattern. It seems verbose at first, but this avoids allocating an array you might not use unless you need it. I'm sure you could make a generic function around this to cut down on the chatter :)
use std::collections::HashMap;
use std::collections::hash_map::Entry::{Occupied, Vacant};
fn foo() {
let mut map = HashMap::<&str, Vec<&str>>::new();
let mut result = match map.entry("whatever") {
Vacant(entry) => entry.insert(Vec::new()),
Occupied(entry) => entry.into_mut(),
};
// Do the work
result.push("One thing");
result.push("Then another");
}
This can also be shortened to or_insert as I just discovered!
use std::collections::HashMap;
fn foo() {
let mut map = HashMap::<&str, Vec<&str>>::new();
let mut result = map.entry("whatever").or_insert(Vec::new());
// Do the work
result.push("One thing");
result.push("Then another");
}
If you want to add your dummy into the map, then this is a duplicate of How to properly use HashMap::entry? or Want to add to HashMap using pattern match, get borrow mutable more than once at a time (or any question about the entry API).
If you don't want to add it, then your code is fine, you just need to follow the compiler error messages to fix it. You are trying to use a keyword as an identifier (ref), and you need to get a mutable reference to dummy (& mut dummy):
use std::collections::HashMap;
fn foo() {
let mut map: HashMap<&str, Vec<&str>> = HashMap::new();
let mut dummy: Vec<&str> = Vec::new();
let f = map.get_mut("whatever").unwrap_or( &mut dummy );
}
fn main() {}
Very often I have obtained an Option<String> from a calculation, and I would like to either use this value or a default hardcoded value.
This would be trivial with an integer:
let opt: Option<i32> = Some(3);
let value = opt.unwrap_or(0); // 0 being the default
But with a String and a &str, the compiler complains about mismatched types:
let opt: Option<String> = Some("some value".to_owned());
let value = opt.unwrap_or("default string");
The exact error here is:
error[E0308]: mismatched types
--> src/main.rs:4:31
|
4 | let value = opt.unwrap_or("default string");
| ^^^^^^^^^^^^^^^^
| |
| expected struct `std::string::String`, found reference
| help: try using a conversion method: `"default string".to_string()`
|
= note: expected type `std::string::String`
found type `&'static str`
One option is to convert the string slice into an owned String, as suggested by rustc:
let value = opt.unwrap_or("default string".to_string());
But this causes an allocation, which is undesirable when I want to immediately convert the result back to a string slice, as in this call to Regex::new():
let rx: Regex = Regex::new(&opt.unwrap_or("default string".to_string()));
I would rather convert the Option<String> to an Option<&str> to avoid this allocation.
What is the idiomatic way to write this?
As of Rust 1.40, the standard library has Option::as_deref to do this:
fn main() {
let opt: Option<String> = Some("some value".to_owned());
let value = opt.as_deref().unwrap_or("default string");
}
See also:
How can I iterate on an Option<Vec<_>>?
You can use as_ref() and map() to transform an Option<String> into an Option<&str>.
fn main() {
let opt: Option<String> = Some("some value".to_owned());
let value = opt.as_ref().map(|x| &**x).unwrap_or("default string");
}
First, as_ref() implicitly takes a reference on opt, giving an &Option<String> (because as_ref() takes &self, i.e. it receives a reference), and turns it into an Option<&String>. Then we use map to convert it to an Option<&str>. Here's what &**x does: the rightmost * (which is evaluated first) simply dereferences the &String, giving a String lvalue. Then, the leftmost * actually invokes the Deref trait, because String implements Deref<Target=str>, giving us a str lvalue. Finally, the & takes the address of the str lvalue, giving us a &str.
You can simplify this a bit further by using map_or to combine map and unwrap_or in a single operation:
fn main() {
let opt: Option<String> = Some("some value".to_owned());
let value = opt.as_ref().map_or("default string", |x| &**x);
}
If &**x looks too magical to you, you can write String::as_str instead:
fn main() {
let opt: Option<String> = Some("some value".to_owned());
let value = opt.as_ref().map_or("default string", String::as_str);
}
or String::as_ref (from the AsRef trait, which is in the prelude):
fn main() {
let opt: Option<String> = Some("some value".to_owned());
let value = opt.as_ref().map_or("default string", String::as_ref);
}
or String::deref (though you need to import the Deref trait too):
use std::ops::Deref;
fn main() {
let opt: Option<String> = Some("some value".to_owned());
let value = opt.as_ref().map_or("default string", String::deref);
}
For either of these to work, you need to keep an owner for the Option<String> as long as the Option<&str> or unwrapped &str needs to remain available. If that's too complicated, you could use Cow.
use std::borrow::Cow::{Borrowed, Owned};
fn main() {
let opt: Option<String> = Some("some value".to_owned());
let value = opt.map_or(Borrowed("default string"), |x| Owned(x));
}
A nicer way could be to implement this generically for T: Deref:
use std::ops::Deref;
trait OptionDeref<T: Deref> {
fn as_deref(&self) -> Option<&T::Target>;
}
impl<T: Deref> OptionDeref<T> for Option<T> {
fn as_deref(&self) -> Option<&T::Target> {
self.as_ref().map(Deref::deref)
}
}
which effectively generalizes as_ref.
Although I love Veedrac's answer (I used it), if you need it at just one point and you would like something that is expressive you can use as_ref(), map and String::as_str chain:
let opt: Option<String> = Some("some value".to_string());
assert_eq!(Some("some value"), opt.as_ref().map(String::as_str));
Here's one way you can do it. Keep in mind that you have to keep the original String around, otherwise what would the &str be a slice into?
let opt = Some(String::from("test")); // kept around
let unwrapped: &str = match opt.as_ref() {
Some(s) => s, // deref coercion
None => "default",
};
playpen
I have a basic (and probably stupid) ownership question. I am trying to create a vector of &str from String values wrapped inside Some(String). I am using an intermediate variable to store the extracted/unwrapped String and it seems I need to define this intermediary variable before the vector in order to satisfy the borrow checker:
Working code:
fn main() {
let a = Some("a".to_string());
let mut val = String::new();
let mut v = Vec::<&str>::new();
if a.is_some() {
val = a.unwrap();
v.push(&val[..]);
}
println!("{:?}", val);
}
Non working code:
fn main() {
let a = Some("a".to_string());
let mut v = Vec::<&str>::new();
let mut val = String::new();
if a.is_some() {
val = a.unwrap();
v.push(&val[..]);
}
println!("{:?}", val);
}
And the compiler errors:
<anon>:9:17: 9:20 error: `val` does not live long enough
<anon>:9 v.push(&val[..]);
^~~
<anon>:4:35: 12:2 note: reference must be valid for the block suffix following statement 1 at 4:34...
<anon>:4 let mut v = Vec::<&str>::new();
<anon>:5 let mut val = String::new();
<anon>:6
<anon>:7 if a.is_some() {
<anon>:8 val = a.unwrap();
<anon>:9 v.push(&val[..]);
...
<anon>:5:32: 12:2 note: ...but borrowed value is only valid for the block suffix following statement 2 at 5:31
<anon>:5 let mut val = String::new();
<anon>:6
<anon>:7 if a.is_some() {
<anon>:8 val = a.unwrap();
<anon>:9 v.push(&val[..]);
<anon>:10 }
...
error: aborting due to previous error
playpen: application terminated with error code 101
The playpen code
The question is: why do I have to define the val variable before the vector v? As I see it, val scope is the same as v scope, or am I missing something?
Bindings are dropped in reverse order of declaration, i.e. the most recently declared thing is destroyed first. Specifically, in the code that doesn't work, the destructor of val runs before the destructor of v. Without careful consideration of what Vec<&str>::drop() does, this is not safe: It could for example try to look at the contents of the string slices it contains, despite the fact that the String from which they derive is already destroyed.
Vec doesn't actually do that, but other legitimate types do something along those lines. Previously it was impossible to safely implement Drop for types that contain lifetimes/borrowed pointers. A relatively recent change makes it safe by introducing these additional restrictions.
Note that if you declare let v, val; or let val, v; and later assign, the two bindings do have the same lifetime, so it's not impossible to have two variables of the same lifetime.