Read_line() don´t wait for users input in loop. Rust - loops

What program shoud do?
Read two numbers separated by space from terminal e.g. "10 8".
Split the string to Vec<&str>.
Parse Vec[0] and Vec[1] to num1 and num2.
Read (math) operation from terminal and parse it.
Print the "math" and result. (10 * 80 = 80)
Catch almost every error in steps 2. - 4. and run try_again function.
If all goes right than run try_again function with "Success" msg
(the main function is there only for context,
the problem is in try_again function)
use std::io::{self, Write, Read, Error, ErrorKind};
const ERR_MSG_STDIN_READ: &str = "Problem with getting input";
const ERR_MSG_STDOUT_FLUSH: &str = "Problem with flushing stdout";
fn main() {
loop {
clear_terminal();
print!("Write 2 ints separated by space: ");
let mut nums_string = String::new();
io::stdout().flush().expect(ERR_MSG_STDOUT_FLUSH);
io::stdin().read_line(&mut nums_string).expect(ERR_MSG_STDIN_READ);
let nums_str_vec: Vec<&str> = nums_string.trim()
.split(' ').collect();
let num1: i32 = match nums_str_vec[0].parse() {
Ok(num) => num,
Err(_) => match try_again("Error") {
true => continue,
false => break
}
};
let num2: i32 = match nums_str_vec[1].parse() {
Ok(num) => num,
Err(_) => match try_again("Error") {
true => continue,
false => break
}
};
print!("Write one of these[+,-,*,/] maths operation for 2 inputed ints: ");
let mut operation_string = String::new();
io::stdout().flush().expect(ERR_MSG_STDOUT_FLUSH);
io::stdin().read_line(&mut operation_string).expect(ERR_MSG_STDIN_READ);
let operation = match operation_string.trim().parse() {
Ok(char) => char,
Err(_) => match try_again("Error") {
true => continue,
false => break
}
};
match operation {
'+' => println!("{} {} {} = {}", num1, operation, num2, num1 + num2),
'-' => println!("{} {} {} = {}", num1, operation, num2, num1 - num2),
'*' => println!("{} {} {} = {}", num1, operation, num2, num1 * num2),
'/' => println!("{} {} {} = {} ({})", num1, operation, num2, num1 / num2, num1 % num2),
_ => match try_again("Error") {
true => continue,
false => break
}
}
io::stdin().read(&mut [0]).expect(ERR_MSG_STDIN_READ);
match try_again("Success") {
true => continue,
false => break
}
}
}
fn clear_terminal() {
print!("{}c", 27 as char);
io::stdout().flush().expect(ERR_MSG_STDOUT_FLUSH);
}
What try_again function shoud do?
Save msg to msg_new for later changing.
Read (y/n) answer from terminal and parse it.
If 'y' return true if 'n' return false.
Catch almost every error in step 2. and continue "self loop" again. (With "Error" msg)
The program (in main function) match returned result of try_again function => true means continue main loop, false means break the loop and end the program.
fn try_again(msg: &str) -> bool {
let mut msg_new = msg;
loop {
println!("The calculation end up with {}.", msg_new);
print!("Do you want to make antoher calculation? (y/n): ");
let mut answer_string = String::new();
stdout().flush().expect(ERR_MSG_STDOUT_FLUSH);
stdin().read_line(&mut answer_string).expect(ERR_MSG_STDIN_READ);
match answer_string.trim().chars().next() {
Some('y') => true,
Some('n') => false,
_ => {
msg_new = "Error";
continue
}
};
}
}
Whats the problem?
What doesn´t work in program?
step 7 (maybe we can say 6 and 7) the problem is in try_again function.
What doesn´t work in try_again function
When the program run try_again("Success");:
In the first loop stdin().read_line(&mut next_string) doesn´t wait for users input and immediately go to _ => arm.
And continue "self loop" again (with "Error" msg)
In the second loop actually wait for input and work but with wrong msg.
Final question
Why the stdin().read_line(&mut next_string) wait for input in secodn round of loop? How to solve it? Thanks for every answer!
I am totally new to rust, please explain everything what you write.
OS: Windows 10, terminal: cmd (Command Promt)

You must check whether a 'y' or a 'n' was entered at all, e.g. with a guard:
fn next(msg: &str) -> bool {
let msg_new = msg;
loop {
clear_terminal();
println!("The calculation end up with {}.", msg_new);
print!("Do you want to make antoher calculation? (y/n): ");
let mut next_string = String::new();
stdout().flush().expect(ERR_MSG_STDOUT_FLUSH);
match std::io::stdin().read_line(&mut next_string) {
Ok(n) if n > 0 && next_string.chars().next().unwrap() == 'y' => break true,
Ok(n) if n > 0 && next_string.chars().next().unwrap() == 'n' => break false,
Ok(_) => {}, // other input is ignored
Err(err) => panic_any(err),
};
}
}
Btw: next should be reserved for iterators

Related

What is the correct way to loop while error occurs

I am quite new to Rust, and I'm still trying to be sure I understand principles doing a small project creating a server that uses a TcpStream. The server asks for a client for a code, which has to be 8 characters long. If it's not, it shall ask again for a new code. Here is what I have so far :
fn loby_matcher(stream: &mut TcpStream) -> std::io::Result<(String, usize), >{
let mut game_code = String::new();
let mut reader = BufReader::new(stream);
let len : usize = reader.read_line(&mut game_code)?;
match len {
8 => len,
_ => return Err(Error::new(ErrorKind::Other, "wrong game code")),
};
Ok((game_code, len))
}
pub fn create_game_matcher(mut stream: TcpStream) -> std::io::Result<()>{
println!("Creating a new game! Waiting for the code.");
let game_code: String;
let len: usize;
loop {
(game_code, len) = match loby_matcher(&mut stream) {
Ok(game_code) => break (game_code, len),
Err(e) => {
stream.write("Wrong code\n".as_bytes());
(String::new(),0)
}
};
};
println!("Received : {} size {}", game_code, len);
// println!("Closing connection now I guess?");
Ok(())
}
fn start_server(address: &str, port: &str) -> std::io::Result<()>{
let listener = TcpListener::bind(format!("{}:{}", address, port))?;
for stream in listener.incoming() {
// not using ? as we do not want to stop the server for wrong connection
let stream = stream.unwrap();
thread::spawn(move || {
create_game_matcher(stream);
});
}
Ok(())
}
The loop asking for a new code when an error occurs seems wrong, but I cannot figure out a way of doing that.
Also, I know I should create a custom error in order to be able to make a difference between a wrong code and an I/O error on my TCPstream, which I will do later.
What would be the right way of looping while a specific error occurs?
Edit
If you see anything wrong in the code, do not hesitate to tell me how I could improve it.
I'm assuming you're asking how to resolve the issue in create_game_matcher. If so, then you need to move your assignment further out.
To compare the error inside io::Error, can be cumbersome. You can check the kind using e.kind() == ErrorKind::Other. However, the actual error could be anything, and doesn't require implementing PartialEq for &str. The easiest is probably just to do e.to_string() == "wrong game code".
But like you mention, the most idiomatic approach would be to create a your own error type.
pub fn create_game_matcher(mut stream: TcpStream) -> std::io::Result<()> {
println!("Creating a new game! Waiting for the code.");
let (game_code, len) = loop {
match loby_matcher(&mut stream) {
Ok(game_code) => break game_code,
// Err(e) if e.kind() == ErrorKind::Other && e.to_string() == "wrong game code" => {
// or just (for ease of use)
Err(e) if e.to_string() == "wrong game code" => {
stream.write("Wrong code\n".as_bytes())?;
}
Err(e) => return Err(e),
};
};
println!("Received : {} size {}", game_code, len);
// println!("Closing connection now I guess?");
Ok(())
}
Since you want to loop until loby_matcher succeeds, then (String::new(), 0) can be removed. You also forgot to check whether stream.write failed, so I've added an ?.
Alternatively, given that you always break after the assignment, you could also express it like this:
pub fn create_game_matcher(mut stream: TcpStream) -> std::io::Result<()> {
println!("Creating a new game! Waiting for the code.");
let game_code: String;
let len: usize;
loop {
match loby_matcher(&mut stream) {
Ok((game_code_, len_)) => {
game_code = game_code_;
len = len_;
break;
}
Err(e) if e.to_string() == "wrong game code" => {
stream.write("Wrong code\n".as_bytes())?;
}
Err(e) => return Err(e),
};
}
println!("Received : {} size {}", game_code, len);
// println!("Closing connection now I guess?");
Ok(())
}

Comparing arrays with -nan

I have two Double arrays with the equal amount of elements. Both of them contain numbers and some NaN values after trigonometric calculations.
I need to compare every element of the first array to every element of the second, find the greater number, and put it into a new third array. At the end, the third array should contain the same amount of elements as first or second array.
If two Nan are compared, I need to display a specific error message "terrible error" at that exact index. So I guess the third array should be String to be able to display both numbers and error messages. If a Double number is compared to NaN, the Double number should always be chosen as the greater.
How do I do all of that?
Here is my code:
import Foundation
var initValue = Double()
var finalValue = Double()
var stepValue = Double()
while true {
print("Enter the starting number of the range")
if let number = readLine(), Double(number) != nil {
initValue = Double(number)!
break
} else {
print("Enter the correct number!")
}
}
while true {
print("Enter the end value of the range")
if let number = readLine(), Double(number) != nil, Double(number)! > initValue {
finalValue = Double(number)!
break
} else {
print("Enter the correct number, which is greater than starting number of the range!")
}
}
while true {
print("Enter delta")
if let number = readLine(), Double(number) != nil {
stepValue = Double(number)!
break
} else {
print("Enter the correct number!")
}
}
var trueArray = [Double]()
for number in stride(from: initValue, through: finalValue, by: stepValue) {
trueArray.append(number)
}
func calcLn () -> [Double] {
let calculatedArray = trueArray.map { log(1-46/sin($0)) }
return calculatedArray
}
func calcTan () -> [String] {
let calculatedArray = trueArray.map { (tan($0)/46) }
return calculatedArray
}
You can use zip to iterate over your 2 arrays concurrently, and then map to transform the pairs of elements into outputs, as you would like them. It's useful to switching on (ln.isNaN, tan.isNaN), and use that to express the various cases and what their results should be.
Here's a rough start:
import Darwin
func promptForDouble(
initialMessage: String,
errorMessage: String,
acceptanceCriteria isAcceptable: (Double) -> Bool = { _ in true }
) -> Double {
print(initialMessage)
while true {
if let number = readLine().flatMap(Double.init), isAcceptable(number) {
return number
}
print(errorMessage)
}
}
let initValue = promptForDouble(
initialMessage: "Enter the starting number of the range",
errorMessage: "Enter the correct number!"
)
let finalValue = promptForDouble(
initialMessage: "Enter the end value of the range",
errorMessage: "Enter the correct number, which is greater than starting number of the range!",
acceptanceCriteria: { initValue < $0 }
)
let stepValue = promptForDouble(
initialMessage: "Enter delta",
errorMessage: "Enter the correct number!"
)
// TODO: give these functions better names!
func calcLn(_ input: [Double]) -> [Double] {
return input.map { log(1 - 46/sin($0)) }
}
func calcTan(_ input: [Double]) -> [Double] {
return input.map { tan($0) / 46 }
}
func mergeResults(lns: [Double], tans: [Double]) -> [Double?] {
return zip(lns, tans).map { ln, tan -> Double? in
switch (ln.isNaN, tan.isNaN) {
case ( true, true): return nil // Return nil to express error. Don't introduce Strings yet.
case (false, true): return ln
case ( true, false): return tan
case (false, false): return max(ln, tan)
}
}
}
func printResults(_ a1: [Double], _ a2: [Double], _ a3: [Double?]) {
for (a, (b, c)) in zip(a1, zip(a2, a3)) {
let resultString = c.map(String.init) ?? "terrible error" // Note: Strings are only introduced at the UI/presentation layer
print("ln: \(a),\ttan: \(b),\tresult: \(resultString)")
}
}
// TODO: give these arrays better names!
let inputs = Array(stride(from: initValue, through: finalValue, by: stepValue))
let array1 = calcLn(inputs)
let array2 = calcTan(inputs)
let array3 = mergeResults(lns: array1, tans: array2)
printResults(array1, array2, array3)

Pattern match an array of Regex's in Scala

I have an array of regex's in Scala, and am trying to verify that a message body contains anything in the regex. However, in the messageBody variable I'm getting a Pattern type is incompatible with given type, expected Array[Regex], found Array[String]. How can I pass in a proper case?
A few other posts have suggested using Pattern but that hasn't worked in my case.
val messageBody: Array[String] = message.body.split(' ')
val unsubscribeTriggers: Array[Regex] = Array("unsubscribe/i".r, "stop/i".r, "stopall/i".r, "cancel/i".r, "end/i".r, "quit/i".r)\
if (messageBody.length == 1) {
unsubscribeTriggers match {
case `messageBody` => true
case _ => false
}
}
You expect too much magic from the compiler. If you have a collection of regexes, you have to check yourself against every element:
val unsubscribeTriggers: Array[Regex] = Array("(?i)unsubscribe".r, "(?i)stop".r)
val body = "sToP"
val matchFound = unsubscribeTriggers.exists { p: Regex =>
body match {
case p() => true
case _ => false
}
}
println(matchFound)
Regex is made case insensitive by adding (?i) at the start. Try it.
This will tell you if any of the Regex patterns match the 1st element in the massageBody array.
unsubscribeTriggers.exists(_.findAllIn(messageBody.head).nonEmpty)
But I don't think your regex patterns do what you want. What is the /i extension? If you mean for it to ignore case, it won't. It's also not a very efficient or Scala-esque way to do it. Instead of many smaller regex patterns, combine them into a single test.
val unsubscribeTriggers: Array[String] =
Array("unsubscribe/i", "stop/i", "stopall/i", "cancel.i", "end/i", "quit/i")
val TriggersRE = unsubscribeTriggers.mkString("(", "|", ")").r
messageBody.head match {
case TriggersRE(_) => true
case _ => false
}
update
If you just need to compare strings, ignoring case, then try this.
val unsubscribeTriggers: Array[String] =
Array("unsubscribe", "stop", "stopall", "cancel", "end", "quit")
unsubscribeTriggers.exists(messageBody.head.equalsIgnoreCase)
If you want to test if any element in massageBody matches any element in unsubscribeTriggers then try this.
unsubscribeTriggers.exists(t => messageBody.exists(t.equalsIgnoreCase))
You can match on regex individual variables pretty cleanly also.
val messageBody: Array[String] = message.body.split(' ')
val unsubscribe = "(?i)unsubscribe".r
val stop = "(?i)stop".r
val stopall = "(?i)stopall".r
val cancel = "(?i)cancel".r
val end = "(?i)end".r
val quit = "(?i)quit".r
val shouldUnsubscribe = messageBody.headOption.exists {
case unsubscribe() => true
case stop() => true
case stopall() => true
case cancel() => true
case end() => true
case quit() => true
case _ => false
}

Replacing text won't go correctly with encoding and decoding

I am currently using 2 arrays:
let letters:[Character] =
[" ","a","b","c","d","e","f","g","h","i","j","k","l","m","n","o","p","q","r","s","t","u","v","w","x","y","z","!","#","#","$","%","^","&","*","(",")","1","2","3","4","5","6","7","8","9","0","A","B","C","D","E","F","G","H","I","J","K","L","M","N","O","P","Q","E","S","T","U","V","W","X","Y","Z"]
let cypher:[Character] = ["o","p","q","r","a","b","c","d","e","f","g","h","i","u","v","w","x","y","z","j","k","l","m","n","s","t","$","#","#","!","&","%","^","*","("," ",")","5","7","2","9","8","0","1","3","2","4","Q","W","E","R","T","Y","U","I","O","P","A","S","D","F","G","H","J","K","L","Z","X","C","V","B","N","M"]
Both 73 characters.
I am using this line of code to encode the inserted text:
var encode:[Character:Character] = [:]
for (index, letter) in letters.enumerated() { encode[letter] = cypher[index] }
let encodeStep1 = String(insertedText.characters.map({ encode[$0] ?? $0 }))
randomNumber = Int(arc4random_uniform(9) + 1)
var encodeStep2 = cypher.rotate(shift: randomNumber)
for (index, letter) in letters.enumerated() { encode[letter] = encodeStep2[index] }
let final = String(encodeStep1.characters.map({ encode[$0] ?? $0 }))
Decode:
var decode:[Character:Character] = [:]
let step1 = cypher.rotate(shift: (randomNumber))
for (index, letter) in step1.enumerated() { decode[letter] = letters[index] }
let step1Decoded = String(insertedEncryptedText.characters.map({ decode[$0] ?? $0 }))
for (index, letter) in cypher.enumerated() { decode[letter] = letters[index] }
let step2Decoded = String(step1Decoded.characters.map({ decode[$0] ?? $0 }))
Rotate function:
extension Array {
func rotate(shift:Int) -> Array {
var array = Array()
if (self.count > 0) {
array = self
if (shift > 0) {
for i in 1...shift {
array.append(array.remove(at: 0))
}
}
else if (shift < 0) {
for i in 1...abs(shift) {
array.insert(array.remove(at: array.count-1),at:0)
}
}
}
return array
}
}
For some very odd reason, number "3" is often shown as a number "9" when decoded. From what I have seen, the problem occurs at step2Decoded. I just can not figure out what I am doing wrong. This is however a part of the code, if I need to post more I can do that.
It's because you have a typo. Your cypher array has two "2"s in it, one at cypher[39] and another 6 indices further at cypher[45]. When you're decoding in the final step you're expecting decoded["2"] to map to the value "3" in the letters array, which it does, but it's getting overwritten when it hits the "2" again setting it to the value 6 indices further in the letters array which has a value of "9".
I assume you want to change that second "2" value in the letters array to "6" instead (since I noticed there was no "6" in there). That would solve your problem.

Parsing text file lines into numbers using std::iter::Iterator map

I'm trying to read and parse a text file in Rust. Each line is a signed integer. I'm able to do it using for line in lines iteration but I'm unable to do it with a iter().map(|l| ...) one-liner. I'm getting a
expected `&core::result::Result<collections::string::String, std::io::error::Error>`,
found `core::result::Result<_, _>`
when I try to pattern match Ok(s) => match s.parse() but I'm unable to get to the bottom of what I am doing wrong. The whole example is below. The code on the bottom is the code that is producing the error.
Can anyone tell what I am doing wrong?
use std::error::Error;
use std::fs::File;
use std::io::BufReader;
use std::io::prelude::*;
use std::path::Path;
fn main() {
// Create a path to the desired file
let path = Path::new("input/numbers.txt");
let display = path.display();
// Open the path in read-only mode, returns `io::Result<File>`
let file = match File::open(&path) {
// The `description` method of `io::Error` returns a string that describes the error
Err(why) => panic!("couldn't open {}: {}", display, Error::description(&why)),
Ok(file) => file,
};
// Collect all lines into a vector
let reader = BufReader::new(file);
let lines: Vec<_> = reader.lines().collect();
// Works.
let mut nums = vec![];
for l in lines {
println!("{:?}", l);
let num = match l {
Ok(s) => match s.parse() {
Ok(i) => i,
Err(_) => 0
},
Err(_) => 0
};
nums.push(num);
}
// Doesn't work!
let nums: Vec<i64> = lines.iter().map(|l| match l {
Ok(s) => match s.parse() {
Ok(i) => i,
Err(_) => 0
},
Err(_) => 0
});
}
Let's look at the complete error message, which points to the error for us:
<anon>:5:9: 5:14 error: mismatched types:
expected `&core::result::Result<&str, ()>`,
found `core::result::Result<_, _>`
(expected &-ptr,
found enum `core::result::Result`) [E0308]
<anon>:5 Ok(s) => match s.parse() {
^~~~~
The compiler is expecting a &Result, but found a Result, and the issue is with the Ok(s) pattern. The type of l is a reference to a Result because you are using iter - which returns an iterator of references to the items in the vector.
The shortest fix is to add a & to the pattern match for the closure variable:
fn main() {
let lines: Vec<Result<_, ()>> = vec![Ok("1"), Ok("3"), Ok("5")];
// HERE V
let nums: Vec<i64> = lines.iter().map(|&l| match l {
Ok(s) => match s.parse() {
Ok(i) => i,
Err(_) => 0
},
Err(_) => 0
}).collect();
println!("{:?}", nums)
}
I also had to add collect to go back to a Vec.
The other change you could make would be to consume the input vector using into_iter and then iterate on each value in the vector:
// HERE V~~~~
let nums: Vec<i64> = lines.into_iter().map(|l| match l {
And for good measure, you could use ok, and_then, and unwrap_or to say the same thing a bit more succinctly:
let nums: Vec<i64> = lines.into_iter().map(|l| {
l.ok().and_then(|s| s.parse().ok()).unwrap_or(0)
}).collect();

Resources