I have not yet used match so I can't understand it very well. I want to try to write a number in the console, but I got the wrong value from match.
My code:
let mut choice = String::new();
let choice = io::stdin().read_line(&mut choice).unwrap();
match choice {
1 => println!("1"),
2 => println!("2"),
3 => println!("3"),
_ => println!("this number doesn't exist"),
};
And the printed value is 2, but I wrote 1 in the console.
read_line doesn't return your input; it returns the number of bytes entered (including the newline at the end). You entered 1 followed by a newline, so 2 characters, so you get 2 as the return value. The actual text is stored in the string you passed a mutable reference to.
let mut choice = String::new();
// Modifies the choice variable in-place.
io::stdin().read_line(&mut choice).unwrap();
// Now choice contains our input, so let's parse it as an integer.
let choice = i32::from_str(&choice).unwrap();
match choice {
...
}
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 want to know if it possible to boolean a String contains a digit between an another digit
to set a variable after
the code i tried
pub fn ram() -> String {
let okmdr = Command::new("wmic").arg("memorychip").arg("get").arg("speed").output().unwrap_or_else(|e| panic!("impossible d'obtenir le type et la vitesse de la RAM"));
let speed = String::from_utf8_lossy(&okmdr.stdout).to_string();
let split: Vec<String> = speed.split_inclusive("Speed").map(|s| s.to_string()).collect();
let splitjsp: Vec<String> = split[1].split(" ").map(|o| o.to_string()).collect();
let jsp = if splitjsp[2].contains(1601..3200) { String::from("DDR4") } else if splitjsp[2].contains(0..1600) { String::from("DDR3") } else { String::from("Unknown")};
jsp
}
the error i got :
let jsp = if splitjsp[2].contains(1601..3200) { String::from("DDR4") } else if splitjsp[2].contains(0..1600) { String::from("DDR3") }...
-------- ^^^^^^^^^^ expected an `FnMut<(char,)>` closure, found `std::ops::Range<{integer}>`
|
required by a bound introduced by this call
Your English is a bit garbled, so let me see if I got that right: You want to check if some number is contained in an interval, but that number is currently stored in a string?
Well, currently your code is calling str::contains which can check whether a string contains e.g. a substring or character. For example, you can test whether "sd" is contained in "asdf" (yes). That is not what you want.
You'll have to use
str::parse
Range::contains
Do note that a Rust range a..b contains a, but doesn't contain b. If you want a range that contains both ends, you want a..=b.
So you might do something like:
let jsp2 = splitjsp[2].parse().expect("JSP output format: expected integer in line 3");
if (1601..=3200).contains(&jsp2) {
…
but it might be more elegant to use a match:
match jsp2 {
0..1601 => …,
1601..3201 => …,
_ => …,
}
Playground
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
In the book, rustaceans-to-be get to build a guessing game. In this guessing game, there's the following snippet:
let mut guess = String::new();
io::stdin().read_line(&mut guess)
.expect("Failed to read line");
Um... why would read_line() fail, I thought. And then found out the hard way: Instead of 50 I entered 5ß (German keyboards...) and... read line failed.
So I thought I'd fix it quickly.
match io::stdin().read_line(&mut guess) {
Ok(str) => str,
Err(_) => println!("Please only enter ASCII characters.");
}
That returned a type mismatch: Expected (), found usize
Ah, right! read_line() returns the number of entered bytes in the Ok result. But I don't want to do anything with that information so I replaced the Ok statement from above:
Ok(_) => {},
That works. But is this the right way to do it? I'm basically telling the program to run an empty code block on Ok, which I'd consider bad style in languages like Java, PHP, JavaScript etc.
Since you're only interested in one of the match arms, you can use an if let binding:
if let Err (_) = io::stdin().read_line(&mut guess) {
println!("Please only enter ASCII characters.");
}
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();