This question already has answers here:
How to read an integer input from the user in Rust 1.0?
(8 answers)
Closed 3 months ago.
I'm learning Rust and am trying to add ints and I got this error:
let c = a + b;
| ^
| |
| expected `&str`, found struct `String`
| help: consider borrowing here: `&b`
Here is my code:
use std::io;
fn main() {
let mut a;
let mut b;
io::stdin().read_line(&mut a);
io::stdin().read_line(&mut b);
let c = a + b;
println!("{c}");
}
I think it might be trying to concatenate a and b though I don't know.
I'm trying to add ints and get input from the user in Rust. Though I don't really know how to fix the error.
From the signature of read_line:
pub fn read_line(&self, buf: &mut String) -> Result<usize>;
You can see that the buffer you pass it will be inferred to be a String.
Before you can add your 2 inputs as numbers you have to convert them to numbers first:
let a = a.parse::<i32>().unwrap();
let b = b.parse::<i32>().unwrap();
you'll then run into the issue that you don't have your variables initialized which is an error in Rust so the whole fixed code would look something like this:
use std::io;
fn main() {
let mut a = String::new();
let mut b = String::new();
io::stdin().read_line(&mut a);
io::stdin().read_line(&mut b);
let a = a.parse::<i32>().unwrap();
let b = b.parse::<i32>().unwrap();
let c = a + b;
println!("{c}");
}
Related
I would like to read a file line by line and then process the words. I use HashMap and the entry API for that. However I get a 'borrowed value does not live long enough' error and am puzzled how to fix this.
1 use std::fs::File;
2 use std::io::{BufRead, BufReader};
3 use std::collections::HashMap;
4
5 fn main() {
6
7 let mut wmap: HashMap<_, i32> = HashMap::new();
8 let file = File::open("book1.txt").unwrap();
9 let reader = BufReader::new(file);
10 for (_index, line) in reader.lines().enumerate() {
11 let line = line.unwrap(); // Ignore errors.
12 let words = line.split_whitespace();
13 for word in words {
14 println!("{}.:.{}", _index, word);
15 *wmap.entry(word).or_insert(0) += 1;
16 }
17 }
18
19 }
The error I get is
error[E0597]: `line` does not live long enough
--> example-words.rs:12:17
|
12 | let words = line.split_whitespace();
| ^^^^^^^^^^^^^^^^^^^^^^^ borrowed value does not live long enough
...
15 | *wmap.entry(word).or_insert(0) += 1;
| ---------------- borrow later used here
16 | }
17 | }
| - `line` dropped here while still borrowed
error: aborting due to previous error
For more information about this error, try `rustc --explain E0597`.
I am aware that this is very similar to Borrowed Value Using BufReader and Lines in Extra Function. However I tried to do it all in one main function whereas the other example uses the extra function
read_lines(filename: &str) -> Result<Lines<BufReader<File>>, Error>
Thanks for any help
You are passing a borrowed string slice (&str) to a HashMap that "lives longer" than the borrowed value word. For this to work the borrowed value would need to have the same lifetime as your HashMap OR the HashMap needs to have ownership of the value inside of word. Here's an example:
use std::io;
use std::collections::HashMap;
fn main() {
let mut db = HashMap::new(); //initialize mutable hashmap outside of the loop
loop{
//I start a loop to take in multiple key val arguments from the
//command line but this means each iteration of the loop will
//clean up heap variables and any &str borrowing from these
//variables will be invalid after each iteration and the rust
// borrow checker will let us know if we are trying to access
// these invalid references, hence the compiler error
let mut string = String::new();
io::stdin().read_line(&mut string).unwrap();
let command: Vec<&str> = string.trim().split(" ").collect();
db.insert(command[0], command[1]);
}
}
I end up with the same compiler error:
error[E0597]: `string` does not live long enough
--> main.rs:9:30
|
9 | let command: Vec<&str> = string.trim().split(" ").coll...
| ^^^^^^^^^^^^^ borrowed value does not live long enough
10 | db.insert(command[0], command[1]);
| --------------------------------- borrow later used here
11 | }
| - `string` dropped here while still borrowed
This is because on every iteration of the loop the string slice I intend my HashMap to borrow gets dropped (goes out of scope and is no longer valid) and rust keeps us from having dangling references. Instead change the db.insert(command[0], command[1]) to db.insert(command[0].to_string(), command[2].to_string()). This will convert the &str -> String which will then be "owned" by the HashMap instance and survive for the remainder of the running
program. In your case:
use std::fs::File;
use std::io::{BufRead, BufReader};
use std::collections::HashMap;
fn main() {
let mut wmap: HashMap<_, i32> = HashMap::new();
let file = File::open("book1.txt").unwrap();
let reader = BufReader::new(file);
for (_index, line) in reader.lines().enumerate() {
let line = line.unwrap(); // Ignore errors.
let words = line.split_whitespace();
for word in words {
println!("{}.:.{}", _index, word);
*wmap.entry(word.to_string()).or_insert(0) += 1;
}
}
}
this will compile and run :)
Hope that helps!
As commented by #cdhowie, you need to own the string using word.to_owned().
While it is not an error, Rust naming conventions say that an underscore in front of a variable implies that it is not used, so I renamed _index to index as well.
use std::collections::HashMap;
use std::fs::File;
use std::io::{BufRead, BufReader};
fn main() {
let mut wmap: HashMap<_, i32> = HashMap::new();
let file = File::open("book1.txt").unwrap();
let reader = BufReader::new(file);
for (index, line) in reader.lines().enumerate() {
let line = line.unwrap(); // Ignore errors.
let words = line.split_whitespace();
for word in words {
println!("{}.:.{}", index, word);
*wmap.entry(word.to_owned()).or_insert(0) += 1;
}
}
}
When I try to make my fibonacci evaluator calculate u64 integers the compiler gets upset and refuses to compile. I thought this was built in, but it says "missing crate or module".
error: expected one of `#` or `|`, found `:`
--> src/main.rs:15:12
|
15 | for fib: u64 in r {
| ^
| |
| expected one of `#` or `|`
| help: maybe write a path separator here: `::`
error[E0433]: failed to resolve: use of undeclared crate or module `fib`
--> src/main.rs:15:9
|
15 | for fib: u64 in r {
| ^^^ use of undeclared crate or module `fib`
This code compiles fine without any issues when I use plain old u32 integers:
use std::io;
use std::ops::Range;
fn main() {
println!("Please enter a fibonacci number to evaluate to:");
let mut n: String = String::new();
io::stdin().read_line(&mut n).expect("Not a number");
let mut _n: u32 = n.trim().parse().expect("Please type a number!");
let mut r: Range<u32> = Range { start: 0, end: _n };
let mut fib: u32 = 0;
for fib in r {
fibonacci(fib);
println!("The fibonacci number is {}", fibonacci(fib));
}
}
fn fibonacci(n: u32) -> u32 {
match n {
0 => 1,
1 => 1,
_ => fibonacci(n - 1) + fibonacci(n - 2),
}
}
Why is this happening?
The type of the iteration variable is implied by the iterator. You can't annotate it with a type, because then the iteration variable would just be of the wrong type.
Instead, consider changing the type of r:
let mut r: Range<u64> = Range{start:0,end: _n};
Other notes:
The declaration let mut fib:u32 = 0; is useless; this variable is never used. (for fib creates a new variable named fib scoped to the loop.)
You will have to update fn fibonacci(n: u32) -> u32 to accept and return u64.
You probably should also update _n to be u64.
Consider running your code through rustfmt to fix the wildly inconsistent indentation.
This question already has an answer here:
Do mutable references have move semantics?
(1 answer)
Closed 1 year ago.
here's my problem:
fn main() {
let mut s = String::from("hello");
let s1 = &mut s;
let s2 = s1;
*s2 = String::from("world1");
*s1 = String::from("world2");
println!("{:?}", s);
}
it will result in a compile error because s1 has type &mut String which doesn't implement the Copy trait.
But if I change the code as below:
fn c(s: &mut String) -> &mut String {
s
}
fn main() {
let mut s = String::from("hello");
let s1 = &mut s;
let s2 = c(s1);
*s2 = String::from("world1");
*s1 = String::from("world2");
println!("{:?}", s);
}
it will compile without any error message.
I know when a reference passed to a function, it means the reference borrows the value insteading of owning it.
But in the situation above, it seems like when s1 was passed to fn c and returned immediatelly, s2 borrowed s1 so s1 couldn't be derefed until s2 was out of it's lifetime scope.
So what happened when s1 was passed into the fn c?
From #Denys Séguret's hint, I guess when s1 was passed to fn C, Rust core compiled the parameter s1 to something like &mut *s1, so there was an immutable borrow of s1.
That's why if we put
*s2 = String::from("world1");
behind
*s1 = String::from("world2");
Rust would tell us:
assignment to borrowed `*s1`
And when s2 goes out of it's lifetime scope, there is no borrow of s1 anymore, so s1 can be derefed again.
But I'm not quite sure whether it's a right explanation.
I have the following code:
fn main() {
let mut vec = Vec::new();
vec.push(String::from("Foo"));
let mut row = vec.get_mut(0).unwrap();
row.push('!');
println!("{}", vec[0])
}
It prints out "Foo!", but the compiler tells me:
warning: variable does not need to be mutable
--> src/main.rs:4:9
|
4 | let mut row = vec.get_mut(0).unwrap();
| ----^^^
| |
| help: remove this `mut`
Surprisingly, removing the mut works. This raises a few questions:
Why does this work?
Why doesn't this work when I use vec.get instead of vec.get_mut, regardless of whether I use let or let mut?
Why doesn't vec work in the same way, i.e. when I use let vec = Vec::new(), why can't I call vec.push()?
vec.get_mut(0) returns an Option<&mut String>, so when you unwrap that value you will have a mutable borrow of a String. Remember, that a let statement's left side is using pattern matching, so when your pattern is just a variable name you essentially say match whatever is on the right and call it name. Thus row matches against &mut String so it already is mutable.
Here's a much simpler and more straightforward example to illustrate the case (which you can try in the playground):
fn main() {
let mut x = 55i32;
dbg!(&x);
let y = &mut x; // <-- y's type is `&mut i32`
*y = 12;
dbg!(&x);
}
This question already has answers here:
What's an idiomatic way to print an iterator separated by spaces in Rust?
(4 answers)
Closed 3 years ago.
How do I convert an Iterator<&str> to a String, interspersed with a constant string such as "\n"?
For instance, given:
let xs = vec!["first", "second", "third"];
let it = xs.iter();
One may produce a string s by collecting into a Vec<&str> and joining the result:
let s = it
.map(|&x| x)
.collect::<Vec<&str>>()
.join("\n");
However, this unnecessarily allocates memory for a Vec<&str>.
Is there a more direct method?
You could use the itertools crate for that. I use the intersperse helper in the example, it is pretty much the join equivalent for iterators.
cloned() is needed to convert &&str items to &str items, it is not doing any allocations. It can be eventually replaced by copied() when rust#1.36 gets a stable release.
use itertools::Itertools; // 0.8.0
fn main() {
let words = ["alpha", "beta", "gamma"];
let merged: String = words.iter().cloned().intersperse(", ").collect();
assert_eq!(merged, "alpha, beta, gamma");
}
Playground
You can do it by using fold function of the iterator easily:
let s = it.fold(String::new(), |a, b| a + b + "\n");
The Full Code will be like following:
fn main() {
let xs = vec!["first", "second", "third"];
let it = xs.into_iter();
// let s = it.collect::<Vec<&str>>().join("\n");
let s = it.fold(String::new(), |a, b| a + b + "\n");
let s = s.trim_end();
println!("{:?}", s);
}
Playground
EDIT: After the comment of Sebastian Redl I have checked the performance cost of the fold usage and created a benchmark test on playground.
You can see that fold usage takes significantly more time for the many iterative approaches.
Did not check the allocated memory usage though.
there's relevant example in rust documentation: here.
let words = ["alpha", "beta", "gamma"];
// chars() returns an iterator
let merged: String = words.iter()
.flat_map(|s| s.chars())
.collect();
assert_eq!(merged, "alphabetagamma");
You can also use Extend trait:
fn f<'a, I: Iterator<Item=&'a str>>(data: I) -> String {
let mut ret = String::new();
ret.extend(data);
ret
}