I'm rewriting an average calculator from my Java textbook in Rust. It uses a loop to ask for several inputs, but I think I've run into an issue with ownership (as there's a let grade inside the loop), so I'm trying to get a better understanding.
When I declare grade outside the loop, only the first input value is recorded: every subsequent grade value becomes 0, regardless of input. If I declare grade inside the loop, every input value is recorded properly.
Is there a way to use borrowing that will let me declare grade outside the loop, and still have this little program work?
use std::io;
fn main() {
let mut total: u32 = 0;
let mut count: u32 = 0;
let mut grade = String::new(); //This one
while count < 5 {
println!("Enter number:");
io::stdin().read_line(&mut grade)
.expect("failed.");
let grade: u32 = match grade.trim().parse() {
Ok(num) => num,
Err(_) => 0,
};
total = total + grade;
count = count + 1;
}
println!("Tot: {}\nAvg: {}", total, total as f64/count as f64);
}
read_line appends to the string you're reading into, so if you input 1 and then 2, grade will contain 1\n2 or something to that effect. This can't be parsed as an integer so the match below will always return 0. The solution is to empty the string before reading: grade.clear();
Related
I have the following function. It is given a file. It should return a random line from the file as a string.
fn get_word(word_list: File) -> String {
let reader = BufReader::new(word_list);
let lines = reader.lines();
let word_count = lines.count();
let y: usize = thread_rng().gen_range(0, word_count - 1);
let element = lines.nth(y);
match element {
Some(x) => println!("Result: {}", x.unwrap()),
None => println!("Error with nth"),
}
let word = String::new(""); // Once the error is gone. I would create the string.
return word;
}
But I keep getting this error:
93 | let lines = reader.lines();
| ----- move occurs because `lines` has type `std::io::Lines<BufReader<File>>`, which does not implement the `Copy` trait
94 | let word_count = lines.count();
| ------- `lines` moved due to this method call
...
99 | let element = lines.nth(y);
| ^^^^^^^^^^^^ value borrowed here after move
|
I am new to Rust and have been learning by try and error. I don't know how to access the data after I have called the count function. If there is another method to accomplish what I want, I would gladly welcome it.
The .count() method consumes the iterator. From the documentation
Consumes the iterator, counting the number of iterations and returning it.
This method will call next repeatedly until None is encountered, returning the number of times it saw Some. Note that next has to be called at least once even if the iterator does not have any elements.
In other words, it reads the file content and discards it. If you want to get the Nth line, then you have to re-read the file using another iterator instance.
If your file is small, you can save the read lines in a vector:
let lines = reader.lines().collect::<Vec<String>>();
Then the length of the vector is the number of lines and you can avoid re-reading the file, but if it's a large file you may end-up crashing with "out of memory" error. In that case you should re-read the file content, or use a better strategy such as indexing where the new lines are, so you can jump straight to the new line, without having to re-read a lot of data.
The value returned by lines is an iterator, which reads the file sequentially. To count the number of lines, the iterator is consumed: self is taken by value; ownership is transferred into the count() function. So you can't rewind and then request the nth line.
The easiest solution is to read all the lines into a vector:
let lines = reader.lines().collect::<Vec<String>>();
let word_count = lines.len();
let y: usize = thread_rng().gen_range(0, word_count - 1);
let word = lines[y].clone();
return word;
Notice the clone call: you can't simply write return lines[y]; because you'd be borrowing the string from the vector, but the vector is destroyed as soon as the function returns. By returning a clone of the string, this is avoided.
(to_owned or even to_string would also work. You can also avoid a copy by using swap_remove; I'm not sure there is a more elegant way to move one element from a vector and discard the rest.)
Note that counting the lines and then selecting one of them requires you to either rewind the iterator and go through it twice (once to count and once to select), or to store everything in memory first (e.g. with .collect::<Vec<_>>). Selecting a random line from the list can however be done in a single pass by randomly choosing on each line whether to keep the currently selected line or replacing it with the latest read line:
fn get_word(word_list: File) -> String {
let reader = BufReader::new(word_list);
let lines = reader.lines();
let mut selected = lines.next().unwrap();
let mut count = 0;
for l in lines {
count += 1;
if thread_rng().gen_range (0, count) == 0 {
selected = l;
}
}
match selected {
Ok(x) => return x,
Err(_) => {
print!("Error get_word");
return String::new();
}
}
}
Or of course the simplest way is to just use choose:
fn get_word(word_list: File) -> String {
use rand::seq::IteratorRandom;
let reader = BufReader::new(word_list);
match reader.lines.choose (thread_rng()) {
Some (Ok (x)) => return x,
_ => {
print!("Error get_word");
return String::new();
}
}
}
In order to solve this problem I used the solution given of using .collect::<Vec<String>> but the whole solution needs a little more work. At least in my case.
First: .lines returns a Iterator of type Result<std::string::String, std::io::Error>.
Second: To access the value of this vector I have to borrow it with &.
Here the working function:
fn get_word(word_list: File) -> String {
let reader = BufReader::new(word_list);
let lines = reader.lines().collect::<Vec<_>>();
let word_count = lines.len();
let y: usize = thread_rng().gen_range(0, word_count - 1);
match &lines[y] {
Ok(x) => return x.to_string(),
Err(_) => {
print!("Error get_word");
return String::new();
}
}
}
I created a empty Mutable String variable using String::new() before a loop start. Then I printed the string value as soon as I entered the loop, changed the type of the same variable to integer u32 by with user input, after trimming all spaces, \n, \r etc..
At the next Iteration of the loop, The value of the variable is back to String and was about to change its type, but when I checked the value of String by printing it, It had some ghost \n and spaces or some ghost characters inherited from the previous integer value.
if the integer is 3 digit, for eg 534 it has 5 characters
if the integer is 1 digit, for eg 3 it has 3 characters
if I give empty value as input, The parsing fails it stays as String, but still in next iteration the String has 2 characters.
I created a function to keep track of the type of variable.
use std::io;
//function to return type of a variable
fn type_of<T>(_: &T) -> String {
return format!("{}", std::any::type_name::<T>());
}
fn main() {
let mut guess = String::new();
loop {
println!(
"At start of loop : {},{}",
type_of(&guess),
guess.chars().count()
);
println!("value : {}", guess);
//emptying string
String::clear(&mut guess);
println!(
"after clearing : {},{}",
type_of(&guess),
guess.chars().count()
);
//getting input for string
println!("Enter value :");
io::stdin()
.read_line(&mut guess)
.expect("Failed to read line");
//converted its values to interger u32 after trimming spaces,\n and \r and stuffs like that
let guess: u32 = match guess.trim().parse() {
Ok(a) => a,
Err(b) => {
println!("{}", b);
println!("after reciving error : {}", type_of(&guess));
continue;
}
};
println!("after type conversion : {}", type_of(&guess));
println!("value: {}", guess);
}
}
the output was :
At start of loop : alloc::string::String,0
value :
after clearing : alloc::string::String,0
Enter value :
111
after type conversion : u32
value: 111
At start of loop : alloc::string::String,5
value : 111
after clearing : alloc::string::String,0
Enter value :
1
after type conversion : u32
value: 1
At start of loop : alloc::string::String,3
value : 1
after clearing : alloc::string::String,0
Enter value :
cannot parse integer from empty string
after reciving error : alloc::string::String
At start of loop : alloc::string::String,2
value :
after clearing : alloc::string::String,0
Enter value :
What causes this?
Is there a way to maintain the value before the loop, at the start of every iteration?
or may be maintain the value of Integer from previous iteration and u32 Type at the same time?
I ran into this problem when I was trying to learn rust using "The Book" from rust docs, to be specific when I was trying to mess around with the code from Chapter 2 (Guess a number project).
There is a misunderstanding on how variables work in Rust. Different variables with the same name can exist, a process called shadowing. In this program, we have two variables called guess.
The following simplification of the previous code shows this pattern.
let guess: mut = String::new(); // <-- guess #1, lives outside loop
loop {
guess.clear();
println!("Enter value :");
io::stdin()
.read_line(&mut guess)
.expect("Failed to read line");
// guess #2, lives inside loop
// vvvvv
let guess: u32 = match guess.trim().parse() {
Ok(a) => a,
Err(b) => {
eprintln!("{}", b);
continue;
}
};
println!("value: {}", guess);
}
The first one is always of type String, and the second one is always of type u32. Variables can never change type. What does change is which one can be seen and used in what scope. Since the second guess is only declared in the middle of the loop, mentioning guess before that declaration will mean the first guess, which is the string.
Combining the two facts that:
read_line reads and includes newline characters into the output string;
trim only returns a string slice, without modifying the underlying String value.
then it makes sense that guess will contain trailing newline characters at the beginning of the loop statement after the first iteration.
Is there a way to maintain the value before the loop, at the start of every iteration? or may be maintain the value of Integer from previous iteration and u32 Type at the same time?
With this last question rephrased to mean "a way to maintain the integer value from the previous iteration", then that is possible by giving it a new name and moving it up. In the example below, guess_num is reassigned on each iteration rather than declared each time.
let guess: mut = String::new();
let mut guess_num: u32 = 0;
loop {
println!("Previous number (or 0 if first iteration): {}", guess_num);
guess.clear();
println!("Enter value :");
io::stdin()
.read_line(&mut guess)
.expect("Failed to read line");
guess_num = match guess.trim().parse() {
Ok(a) => a,
Err(b) => {
eprintln!("{}", b);
continue;
}
};
println!("value: {}", guess);
}
See also:
Why do I need rebinding/shadowing when I can have mutable variable binding?
How to ignore the line break while printing a string read from stdin?
The Rust Programming Language, 3.1 Variables and Mutability
I'm trying to solve my first ever project Euler problem just to have fun with Rust, and got stuck on what seems to be an extremely long compute time to solve
Problem:
https://projecteuler.net/problem=757
I came up with this code to try to solve it, which I'm able to solve the base problem (up to 10^6) in ~245 ms and get the expected result of 2,851.
use std::time::Instant;
fn factor(num: u64) -> Vec<u64> {
let mut counter = 1;
let mut factors = Vec::with_capacity(((num as f64).log(10.0)*100.0) as _);
while counter <= (num as f64).sqrt() as _ {
let div = num / counter;
let rem = num % counter;
if rem == 0 {
factors.push(counter);
factors.push(div);
}
counter += 1
}
factors.shrink_to_fit();
factors
}
fn main() {
let now = Instant::now();
let max = 10u64.pow(6);
let mut counter = 0;
'a: for i in 1..max {
// Optimization: All numbers in the pattern appear to be evenly divisible by 4
let div4 = i / 4;
let mod4 = i % 4;
if mod4 != 0 {continue}
// Optimization: And the remainder of that divided by 3 is always 0 or 1
if div4 % 3 > 1 {continue}
let mut factors = factor(i);
if factors.len() >= 4 {
// Optimization: The later found factors seem to be the most likely to fit the pattern, so try them first
factors.reverse();
let pairs: Vec<_> = factors.chunks(2).collect();
for paira in pairs.iter() {
for pairb in pairs.iter() {
if pairb[0] + pairb[1] == paira[0] + paira[1] + 1 {
counter += 1;
continue 'a;
}
}
}
}
}
println!("{}, {} ms", counter, now.elapsed().as_millis());
}
It looks like my code is spending the most amount of time on factoring, and in my search for a more efficient factoring algorithm than what I was able to come up with on my own, I couldn't find any rust code already made (the code I did find was actually slower.) But I did a simulation to estimate how long it would take even if I had a perfect factoring algorithm, and it would take 13 days to find all numbers up to 10^14 with the non-factoring portions of this code. Probably not what the creator of this problem intends.
Given I'm relatively new to programming, is there some concept or programming method that I'm not aware of (like say using a hashmap to do fast lookups) that can be used in this situation? Or is the solution going to involve spotting patterns in the numbers and making optimizations like the ones I have found so far?
If Vec::push is called when the vector is at its capacity, it will re-allocate its internal buffer to double the size and copy all its elements to this new allocation.
Vec::new() creates a vector with no space allocated so it will be doing this re-allocation.
You can use Vec::with_capacity((num/2) as usize) to avoid this and just allocate the max you might need.
I currently do it this way:
// v is a vector with thousands of sorted unsigned int value.
let mut total = 0;
// [...]
// some loop
let a = 5;
if v.iter().any(|&x| x == a as u16) {
total += a;
v.retain(|&x| x != a as u16);
}
// end loop
But it is quite inefficient since I iterate twice over v (although perhaps the compiler would catch this and optimize), isn't it a more elegant way to do it with Rust?
NB: The vector is sorted and contains no duplicate values if it can help
If I understand correctly your request, here a solution:
You say your vector is sorted so you can use binary_search()
And so you can use remove()
fn foo(data: &mut Vec<u16>) -> u64 {
let mut total: u64 = 0;
let mut a = 0;
while data.len() > 0 {
if let Ok(i) = data.binary_search(&a) {
total += data.remove(i) as u64;
}
a += 1;
}
total
}
fn main() {
let mut data = vec![1, 3, 8, 9, 46];
assert_eq!(foo(&mut data), 67);
}
This keep the vector sorted while removing, note that this is a dummy example. If you don't care about sorting you can use swap_remove() but this disallow the use of binary_search().
It's hard to say what would be the better.
To obtain the partial sums of a sequence of integers, I can use scan() on the iterator like this:
let partial: Box<[u32]> =
list
.iter()
.scan(0, |st, elem| {
let ret = *st;
*st += elem;
Some(ret)
})
.collect();
The above code works well, but I'm trying to modify it to give me the total sum as well.
Something like:
let (partial, total): (Box<[u32]>, u32) =
list
.iter()
.scan(0, |st, elem| {
// TODO
})
.collect();
It seems like I would just need to obtain the eventual value of st, and the iterator should already know it's value. However, I can't seem to find a way to retrieve that value, short of doing a second iteration over the whole sequence (e.g. with fold()).
Is there a way to find the partial sums and the total sum in a single pass?
Include the total sum in the scan, but then split off the last value.
use std::iter;
fn main() {
let list = vec![1, 2, 3, 4];
// Add zero at the start to emulate what you had before
let partial: Box<[u32]> = iter::once(0)
.chain(list.iter().scan(0, |st, elem| {
*st += elem;
Some(*st)
}))
.collect();
// unwrap since with the added zero, the slice will always be non-empty
let (total, partial) = partial.split_last().unwrap();
println!("partial sums: {:?}", partial);
println!("total sum: {}", total);
}
(playground)
Or using successors():
fn main() {
use std::iter::successors;
let list = vec![1, 2, 3, 4];
let mut iter = list.iter();
let partial: Vec<_> = successors(Some(0), |n| iter.next().map(|i| n + i)).collect();
// unwrap since with the added zero, the slice will always be non-empty
let (total, partial) = partial.split_last().unwrap();
assert_eq!(partial, &[0, 1, 3, 6]);
assert_eq!(total, &10);
}
(playground)
You must decide what you want to do in the closure.
As it stands in your code, you remember ret = *st which is the accumulator's value before the addition takes place, and you return Some(ret). Thus, the first item you get in the result is currently 0.
If you want the value after the sums, you should just return Some(*st), which is the updated accumulator's value after the addition.