When I try to parse input from read_line() I get ParseIntError { kind: InvalidDigit } - input

I am writing a function that gets an initial length of a vector then reads input until the vector is filled. Things go wrong when I convert the input string into an integer.
fn read_to_vector(prompt: &str) -> Vec<String> {
println!("Enter the number of inital values: ");
let length_string:String = read_value();
let length = length_string.parse::<i32>().unwrap();
println!("{}", prompt);
let mut buffer_vector:Vec<String> = Vec::new();
for _i in 1..(length + 1) {
let buffer_str:String = read_value();
buffer_vector.push(buffer_str);
}
return buffer_vector;
}
fn read_value() -> String {
use std::io;
let mut buf:String = String::new();
io::stdin().read_line(&mut buf).expect("Failed to get input");
return buf;
}
Here is the error message:
thread 'main' panicked at 'called `Result::unwrap()` on an `Err` value: ParseIntError { kind: InvalidDigit }', src/main.rs:8:47
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
I searched online but I could not find anything related.

read_line() does not trim any whitespace. There is probably a newline character at the end of the string, which indeed is not a digit, and this causes parsing to fail. To fix this, trim whitespace from the end of the string before returning it:
return buf.trim_end().to_string();
To save an allocation, you can combine trim_end() with truncate() on the owned string:
let new_len = buf.trim_end().len();
buf.truncate(new_len);
return buf;

Related

How to return error as value from failable function?

The current release of Zig is 0.10.1 at the time of asking this question. The language is not yet stable and is subject to change.
How can I return an error as value from a failable function? I have tried the following, but it returns it as an error instead of returning it as a value:
fn foo_1() !anyerror {
// Returns outer error.
return error.Oops;
}
fn foo_2() !anyerror {
// Returns outer error.
var error_ = error.Oops;
return error_;
}
I would like to return it as value such that the following code to print out the error instead of propagating it when try is evaluated:
fn bar() !void {
var error_ = try foo();
std.debug.print("got error: {any}\n", .{ error_ });
}
I am asking this because I am trying to learn the language. I don't have a concrete use-case for this. However, I am curious if this is possible to do.
A similar related situation is to return an optional value which itself is optional (for example when forwarding the result of a function which returns an optional), but I was already able to find a solution for that:
fn baz_1() ??i32 {
// The outer optional is null.
return null;
}
fn baz_2() ??i32 {
// The inner optional is null.
var value: ?i32 = null;
return value;
}
Is something like that possible for errors as well?
No, it's not allowed in Zig. If you try you'll get an error like "error union with payload of error set type 'error{Def}' not allowed".
However, you can wrap the error in a struct:
const std = #import("std");
const ErrorError = error{
Abc,
};
const ErrorValue = error{
Def,
};
const WrappedError = struct {
err: ErrorValue,
};
fn foo() ErrorError!WrappedError {
return .{
.err = ErrorValue.Def,
};
}
test "returns error value" {
var bar = try foo();
std.debug.assert(bar.err == ErrorValue.Def);
}
error values can be declared with error{...} syntax. see https://ziglang.org/documentation/master/#Errors
pub const Error = error{Bar};
fn foo() error{Bar} {
return error.Bar;
}
// or anyerror
fn foo() anyerror {
return error.Bar;
}

How can I print data in a way that consumes local variables when an assert fails in Rust?

I have some tests which have some variables that hold some important data and I'd like to print their data when an assertion fails. Getting the data I need consumes the variables, so the printing code must own the variables. In this example, I'd want to call dump_foo_data once an assertion fails:
struct Foo();
fn dump_foo_data(f: Foo) {
eprintln!("Behold, Foo data: ");
}
#[test]
fn my_test() {
let f = Foo();
eprintln!("begin");
// do a test
&f;
let success = true;
assert!(success);
// do another test
&f;
let success = false;
assert!(success);
}
I can make a very bad solution by making dump_foo_data non-returning and panic:
fn dump_foo_data(f: Foo) -> ! {
eprintln!("Behold, Foo data: ");
panic!();
}
Then instead of using assert!, I check the failure with an if and maybe call dump_foo_data:
let success = true;
if !success {
dump_foo_data(f);
}
This is too many lines of code, and I need to specify f. In reality, I have more than one variable like f that I need to dump data from, so it's not very nice to list out single relevant local variable in every check.
I couldn't figure out how to write a macro to make this better because I'd still need to pass every relevant local variable to the macro.
I couldn't think of a way to use std::panic either. update_hook would need to take ownership of f, then I couldn't use it in tests.
Is there any good way to do this in Rust?
Edit: I've thought of another approach: put each relevant local in an Rc then pass each of those to std::panic::update_hook. I've not confirmed whether this'll work yet.
Edit 2: Maybe I could abuse break to do what I explained with goto in a comment.
One way that doesn't use any macro or shared-interior-mutability-reference magic might be to repossess f:
fn check_or_dump(success: bool, f: Foo) -> Foo {
match success {
true => f,
false => panic!("Behold foo data: {:?}", dump_foo_data(f)),
}
}
You use it like this:
let f = Foo();
let success = true;
let f = check_or_dump(success, f);
let success = false;
let f = check_or_dump(success, f);
// and so on.
Here's a solution without macro or interior mutability and that doesn't require you to list all the variables on each check. It is inspired by this answer:
struct Foo();
fn dump_foo_data(_f: Foo) {
eprintln!("Behold, Foo data: ");
}
#[test]
fn my_test() {
let f = Foo();
let doit = || -> Option<()> {
eprintln!("begin");
// do a test
&f;
let success = true;
success.then_some(())?;
// do another test
&f;
let success = false;
success.then_some(())?;
Some(())
};
if let None = doit() {
dump_foo_data (f);
panic!("Test failure");
}
}
Playground
I've worked out a solution using the panic handler:
use std::rc::Rc;
use std::cell::{Cell, RefCell};
use std::panic::PanicInfo;
thread_local! {
static TL_PANIC_TARGETS: RefCell<Vec<Rc<dyn PanicTrigger>>> = RefCell::new(vec![]);
}
pub trait PanicTrigger {
fn panic_trigger(self: Rc<Self>);
}
pub fn register_panic_trigger<P: PanicTrigger + 'static>(p: Rc<P>) {
TL_PANIC_TARGETS.with(|v: _| {
v.borrow_mut().push(p.clone());
});
}
#[ctor::ctor]
fn set_panic_hook() {
let old_hook = std::panic::take_hook();
std::panic::set_hook(Box::new(move |pi: &PanicInfo| {
run_panic_triggers(pi);
old_hook(pi);
}));
}
fn run_panic_triggers(_: &PanicInfo) {
TL_PANIC_TARGETS.with(|v: _| {
for pt in v.take() {
pt.panic_trigger();
}
});
}
struct Foo();
fn dump_foo_data(_f: Foo) {
eprintln!("Behold, Foo data: ");
}
impl PanicTrigger for Cell<Option<Foo>> {
fn panic_trigger(self: Rc<Self>) {
if let Some(f) = self.take() {
dump_foo_data(f);
}
}
}
#[test]
fn my_test() {
let f = Rc::new(Cell::new(Some(Foo())));
register_panic_trigger(f.clone());
let success = true;
assert!(success);
let success = false;
assert!(success);
}
fn main() { }
Basically, you put the relevant data in an Rc and keep a local reference and put one in TLS for the panic handler. You need to put it in an Option in a Cell so that you can move out of it.
Types that don't need to be owned to print relevant data can be registered too, and you don't need to implement PanicTrigger on a Cell<Option<T>>, just T.
This is thread-safe.
Because the data is so wrapped up, it's harder to manipulate in the test body. But now you can use normal assert!. It's a trade-off.

Return a Result from a for loop or nothing if there are no results

I want to return the Result as shown in below from the for loop. Please help which would be the best way solve this error. I tried the pattern matching with returning None which works. But I need to return Error.
pub fn get_account(&self) -> Result<Keys, Error> {
//PATH is default home directory
let values = match load_json_file(PATH + "/keys.json") {
Ok(account) => Ok(account),
Err(e) => {
return Err(Error::Invalid_Tx(
"The sender address cannot be nil".to_owned(),
))
}
};
let accounts: Vec<Keys> = values.unwrap();
let sender_address = self.sender.unwrap();
for acc in accounts {
if acc.address == sender_address {
return Ok(acc);
};
};
Ok(())
}
expected struct commands::key::Keys, found ()rustc(E0308)
You are trying to return two different types from the same function:
line 15: Ok(acc) is of type Result<Keys, Error>
line 18: Ok(()) has type Result<(), Error>
If "no result" is a valid return value, then you can change the function signature to:
pub fn get_account(&self) -> Result<Option<Keys>, Error>;
And then modify those return values to be Ok(Some(acc)) and Ok(None) respectively.
If "no result" is an error then you need to modify the Error type to include this variant. For example:
enum Error {
Invalid_Tx(String),
NotFound,
}
And return Err(Error::NotFound) at the end.
You can also tidy this function up a lot, by using thiserror, which is a popular crate for defining error types:
use thiserror::Error; // thiserror = "1.0.21"
#[derive(Debug, Error)]
enum Error {
#[error("The sender address cannot be nil")]
InvalidTx,
#[error("The key was not found")]
NotFound,
}
pub fn get_account(&self) -> Result<Keys, Error> {
let accounts: Vec<Keys> = load_json_file(PATH + "/keys.json")
.map_err(|_| Error::InvalidTx)?;
let sender_address = self.sender.unwrap();
accounts
.into_iter()
.find(|acc| acc.address == sender_address)
.ok_or(Error::NotFound)
}
This is better because the Strings in the errors do not need to be allocated, but they are still available as static string slices if they are needed for display. I also got rid of the for loop altogether, which makes the function shorter and cleaner.

Type deduction error when reading from file

According to multiple sources, I believe this is the correct way to read a string from a file:
use std::error::Error;
fn main() {
let path = std::path::Path::new("input.txt");
let file = match std::fs::File::open(&path) {
Err(e) => {
panic!("Failed to read file {}: {}",
path.display(),
e.description())
}
};
let mut s = String::new();
let mut v = Vec::new();
match file.read_to_string(&mut s) {
Err(e) => panic!("Failed to read file contents: {}", e.description()),
}
println!("{}", s);
}
But this code produces an error using Rust 1.17.0 so I must be missing something:
error: the type of this value must be known in this context
--> src/main.rs:16:11
|
16 | match file.read_to_string(&mut s) {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^
You have multiple overlapping issues. Whenever debugging a programming problem, it helps to create a Minimal, Complete Verifiable Example.
Start by commenting out match file.read_to_string(&mut s) { /* ... */ }. Then you will get another error:
error[E0282]: type annotations needed
--> src/main.rs:15:17
|
15 | let mut v = Vec::new();
| ----- ^^^^^^^^ cannot infer type for `T`
| |
| consider giving `v` a type
Comment out that line too, giving:
error[E0004]: non-exhaustive patterns: `Ok(_)` not covered
--> src/main.rs:6:22
|
6 | let file = match std::fs::File::open(&path) {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^ pattern `Ok(_)` not covered
This is your real issue. Result is an enum with two values, Ok and Err. You have to handle all variants in a match.
In this case, it's easiest to use unwrap_or_else:
let file = std::fs::File::open("input.txt").unwrap_or_else(|e| {
panic!(
"Failed to read file {}: {}",
path.display(),
e.description()
)
});
You can remove the unused vector and apply the same unwrap_or_else to the other failure case. You then need to:
Import std::io::Read.
Declare file as mutable.
You can also:
Print an error directly using {}.
Pass a string slice to File::open.
use std::io::Read;
fn main() {
let path = "input.txt";
let mut file = std::fs::File::open(path).unwrap_or_else(|e| {
panic!("Failed to read file {}: {}", path, e);
});
let mut s = String::new();
file.read_to_string(&mut s).unwrap_or_else(|e| {
panic!("Failed to read file contents: {}", e);
});
println!("{}", s);
}
Compare your code against What's the de-facto way of reading and writing files in Rust 1.x? as well.

How to read a C struct from a binary file in Rust? [duplicate]

This question already has answers here:
How to read a struct from a file in Rust?
(3 answers)
Closed 6 years ago.
I have read How to read a struct from file in Rust?, but std::slice::raw::mut_buf_as_slice has been deprecated, hence I want to re-ask this question.
I want to read a struct utmp from /var/run/utmp, and I have tried the following code:
fn read_utmp() -> Utmp {
let mut reader = BufReader::new(File::open("/var/run/utmp").unwrap());
let mut ut = vec![];
let size = mem::size_of::<Utmp>();
reader.take(size as u64).read_to_end(&mut ut);
unsafe {
std::mem::transmute(ut)
}
}
And as expected, the compiler complaint:
error: transmute called with differently sized types: std::vec::Vec (192 bits) to Utmp (3056 bits) [E0512]
How could I do this?
I believe that raw::mut_buf_as_slice was replaced with slice::from_raw_parts_mut.
Note that the following code does not take into account any endianness or padding issues and is intended to be used with POD types. struct utmp should be safe in this case.
Here is a function that can read a struct (of a pod type) from a file:
use std::io::{self, BufReader, Read};
use std::fs::{self, File};
use std::path::Path;
use std::slice;
fn read_struct<T, R: Read>(mut read: R) -> io::Result<T> {
let num_bytes = ::std::mem::size_of::<T>();
unsafe {
let mut s = ::std::mem::uninitialized();
let mut buffer = slice::from_raw_parts_mut(&mut s as *mut T as *mut u8, num_bytes);
match read.read_exact(buffer) {
Ok(()) => Ok(s),
Err(e) => {
::std::mem::forget(s);
Err(e)
}
}
}
}
// use
// read_struct::<Utmp>(reader)
If you want to read all utmp structs from the utmp file, you can execute read_struct multiple times or read all the file at once:
fn read_structs<T, P: AsRef<Path>>(path: P) -> io::Result<Vec<T>> {
let path = path.as_ref();
let struct_size = ::std::mem::size_of::<T>();
let num_bytes = try!(fs::metadata(path)).len() as usize;
let num_structs = num_bytes / struct_size;
let mut reader = BufReader::new(try!(File::open(path)));
let mut r = Vec::<T>::with_capacity(num_structs);
unsafe {
let mut buffer = slice::from_raw_parts_mut(r.as_mut_ptr() as *mut u8, num_bytes);
try!(reader.read_exact(buffer));
r.set_len(num_structs);
}
Ok(r)
}
// use
// read_structs::<Utmp, _>("/var/run/utmp"))