Closed narration-sd closed 5 years ago
Can you please re-file this against https://github.com/rust-lang/book? Thank you!
Actually, I guess this really isn't about the contents of the book, it's more about the diagnostics. So let's leave it here. Sorry for the noise.
good man. And I edited a little to pick up own mis-types...
@steveklabnik I don't see how this is a diagnostics issue as it happens on runtime: someone writing this code from scratch would either have to deal with parse
returning a Result::Err
or know enough about what expect
/unwrap
is doing.
@narration-sd the book is using a blunt tool for error handling in order to keep the code simple. It is a very reasonable tradeoff. In real code you would deal with the result of
let guess: Result<u32, ParseIntError> = guess.trim().parse();
let guess: u32 = match guess {
Ok(val) => val,
Err(ParseIntError { kind: InvalidDigit}) => {
println!("Por favor, escriba un número!");
::std::process::exit(1);
}
Err(err) => {
println!("Se ha encontrado un error: {:?}", err);
::std::process::exit(1);
}
};
instead of failing outright when that parse
has failed, as in the book:
let guess: u32 = guess.trim().parse().expect("Please type a number!");
There's no line number for the code error -- this is basic, right?
This is because it isn't a compile time error, and there are reasons for panics to not carry more information than they already do.
'panicked' ?? come on...
It is the name of the unwinding mechanism in rust, the result of calling panic!()
or expect
on a Result
.
it's not an invalid digit, it's an invalid type String trying to be digits....you should say so
The error is pointing that the valid String
has invalid digits when trying to parse it as an u32
.
the RUST_BACKTRACE is actually an env var, thus unclear
Perfectly fair.
the 'better' error message is just wrong. The input isn't bad because it's something wrong in the String; it's bad because the shadowed var can't take a String at all...
I believe you might be indeed misunderstanding where the problem is. It is not a type level problem, the string is still a valid string, the error is being caused when trying to call parse
on a String
that holds something that cannot be converted to a u32
. If you remove the expect()
call you will see that the result of parse
can either be Ok(u32_val)
or Err(IntParseErr)
. The later is the one being represented as the string invalid digit found in string
.
I believe there are two actionable points are:
RUST_BACKTRACE
is an env variable, perhaps even showing how to set it with the previous unsuccessful call.hmm hmm. Nice analysis, @estebank; however, it doesn't seem in important regards to quite fit.
guess
placed outside the loop, it is no longer possible to succeed after the first time it is set by read_line() and then 'shadowed' by the parse().guess
. guess
contains after it couldn't be successfully written again by read_line(). Or at least that is the appearance of what happens...Here are a few things I thought of after the original issue:
Thanks, and Rust is interesting... Clive p.s. read_line() -- déjà vu, feel like I'm back in Pascal for a moment ;)
It would be useful to have a copy of the code that was causing you trouble, that way we could devise further improvements to diagnostics that could help other newcomers.
Sure -- as follows.
use std::io;
use std::cmp::Ordering;
use rand::Rng;
fn main() {
println!("Adivina el número!");
let secret_number = rand::thread_rng().gen_range(1, 101);
println!("El número secreto es: {}", secret_number);
// here's the problem start -- initialization thus first type set outside of the loop
let mut guess = String::new();
loop {
println!("Por favor ingrese su conjetura.");
io::stdin().read_line(&mut guess)
.expect("Error al leer la línea...");
println!("Usted lo adivinó: {}", guess);
// let guess: u32 = guess.trim().parse()
// .expect("Por favor, escriba un número!");
let guess: u32 = match guess.trim().parse() {
Ok(num) => num,
Err(err) => {
println!("Número malo! {:?} : Inténtalo de nuevo...", err);
continue
},
};
match guess.cmp(&secret_number) {
Ordering::Less => println!("¡Demasiado pequeña!"),
Ordering::Greater => println!("¡Demasiado grande!"),
Ordering::Equal => {
println!("¡Tú ganas!");
break;
},
}
}
}
n.b. I've possibly made less clear by explaining too much, but the basis problem appears to be that once guess
has been shadowed, it can no longer accept a string.
Then the parse fails, not because of an actual improper number, but because guess
now is flagged, or contains something parse can't handle.
I did pick up your {:?} for better general error information...thanks.
The problem you have here is not one of shadowing, but it is indeed a wart of the api:
use std::io;
use std::cmp::Ordering;
use rand::Rng;
fn main() {
println!("Adivina el número!");
let secret_number = rand::thread_rng().gen_range(1, 101);
println!("El número secreto es: {}", secret_number);
// here's the problem start -- initialization thus first type set outside of the loop
let mut guess = String::new();
loop {
guess.clear() // <- NOTE THIS: you have to clear the `guess` buffer after every use, otherwise `read_line` will *append* to it
println!("Por favor ingrese su conjetura.");
io::stdin().read_line(&mut guess)
.expect("Error al leer la línea...");
println!("Usted lo adivinó: {}", guess);
// let guess: u32 = guess.trim().parse()
// .expect("Por favor, escriba un número!");
let guess: u32 = match guess.trim().parse() {
Ok(num) => num,
Err(err) => {
println!("Número malo! {:?} : Inténtalo de nuevo...", err);
continue
},
};
match guess.cmp(&secret_number) {
Ordering::Less => println!("¡Demasiado pequeña!"),
Ordering::Greater => println!("¡Demasiado grande!"),
Ordering::Equal => {
println!("¡Tú ganas!");
break;
},
}
}
}
You're appending to the buffer guess
, you need to clear it. If you look at the output, if you try 10\n
and then 15\n
, the output will be Usted lo adivinó: 10\n15\n
.
Filed https://github.com/rust-lang/rust/issues/56734 to deal with the env var feedback.
Fair enough -- though as you appear to recognize, the last things I'd expect from semantics of an api function named read_line are:
guess
in this case -- instead of freshly writing it...Then, github just updated on-the-page, so see you've picked up on first point, though 'identifying' seems it's short of the mark as far as how this should act, unless as ever I am missing something.
Thanks, Esteban, and hope I'm contributing to development of clarity of Rust here. C.
added to, if you are reading only email...
ok, last on this I think. I had a look through old postings on Rust w/r/t read_line's behavior and related matters, not to say the doc.
I guess all this I ran into is intentional, in stdio. And also non-intuitive to many of experience.
Ok, got that off my chest, and maybe I have to accept Rust as a specific-corners sort of language, useful for its uses however.
Thanks for your patience to see this through, @estebank , and indeed shadowing itself seems to work as I'd thought it should in this case -- will have to read more about that in doc.
Regards, Clive
p.s. for other unenlightened pre-rusters, this would have been helpful to go through.
It does rather illustrate that I wasn't out on a limb to find things to be unusual...but also will help to get 'there'...
http://dtrace.org/blogs/ahl/2015/06/22/first-rust-program-pain/
The various issues with read_line() are right up front, and he substitutes around them. As I don't think they're very comfortable to resolve.
The problem you have here is not one of shadowing, but it is indeed a wart of the api:
use std::io; use std::cmp::Ordering; use rand::Rng; fn main() { println!("Adivina el número!"); let secret_number = rand::thread_rng().gen_range(1, 101); println!("El número secreto es: {}", secret_number); // here's the problem start -- initialization thus first type set outside of the loop let mut guess = String::new(); loop { guess.clear() // <- NOTE THIS: you have to clear the `guess` buffer after every use, otherwise `read_line` will *append* to it println!("Por favor ingrese su conjetura."); io::stdin().read_line(&mut guess) .expect("Error al leer la línea..."); println!("Usted lo adivinó: {}", guess); // let guess: u32 = guess.trim().parse() // .expect("Por favor, escriba un número!"); let guess: u32 = match guess.trim().parse() { Ok(num) => num, Err(err) => { println!("Número malo! {:?} : Inténtalo de nuevo...", err); continue }, }; match guess.cmp(&secret_number) { Ordering::Less => println!("¡Demasiado pequeña!"), Ordering::Greater => println!("¡Demasiado grande!"), Ordering::Equal => { println!("¡Tú ganas!"); break; }, } } }
You're appending to the buffer
guess
, you need to clear it. If you look at the output, if you try10\n
and then15\n
, the output will beUsted lo adivinó: 10\n15\n
.@estebank How is this code working after first iteration if we are shadowing the guess to u32?
This is due to a facet you are promoting of Rust language, but causes consternation in what is supposed to be an easy-entry example.
The problem occurs when you are invited to 'just' add a loop to your code-so-far, to allow multiple guesses. What you are likely to get if you follow other language practices is a nasty error:
This occurs if you don't include the line
let mut guess = String::new();
within the loop, which is likely if you have a lot of experience, and are in a hurry, especially, though no doubt newer persons could also do it. If you go on to include the better error handling later in the lesson, you'll get an equally obscure error:invalid digit found in string
.In the morning, it seems this occurs because of the type-shadowing which has made the var
guess
no longer able to take a String. I would also say that the error messages, either of them, are very uninformative about the real problem.What can you do?
I was really surprised to see such a highly touted newer language being so raw. It makes me a bit wary about the intended reason to use it, Web Assembly. I hope you'll improve, thanks.