My rust macro doesn't want to accept this let statement - input

I've been trying to make a macro that functions similar to Python's input function.
Rather than write the stdin completely every time I wanted to automate it somewhat, and combine println! so I could kill 2 birds with one stone.
Essenitally, if someone passes in an argument it'll print a string then ask for input, if they don't it will just ask for input from the terminal.
#[macro_export]
macro_rules! input {
($a:expr) => {
println!("{}", $a);
let mut input = String::new();
std::io::stdin().read_line(&mut input).unwrap();
return $input
};
(_) => {
let mut input = String::new();
std::io::stdin().read_line(&mut $input).unwrap();
return $input
};
}
I keep getting an error on the let statement and just don't know how to continue because I don't know the macro syntax well.
I posted the whole code block beacuse on the second match expression I was trying to make a match for when there were no arguments but i'm not sure if I did it correctly.
Sometimes the error messages brought me to github pages and I encounter randomed bugs, so i'm just confused how to continue furhter
It would be most appreciated if someone could help me fix the let statement, and i'd like to apologize for any inconvience.

#[macro_export]
macro_rules! input {
($a:expr) => {{
println!("{}", $a);
let mut input = String::new();
std::io::stdin().read_line(&mut input).unwrap();
input
}};
(_) => {{
let mut input = String::new();
std::io::stdin().read_line(&mut $input).unwrap();
input
}};
}
fn main() {
let foo = input!("Blargh");
}
You need the extra curly braces. The first pair is to enclose what the macro should expand to. The second pair is because the macro should expand to a code block.
Then we get rid of return because a macro isn't a function. Instead, it just expands to some code, and the value of a code block is just the last expression (without a ;).

Related

Rust: Read and map lines from stdin and handling different error types

I'm learning Rust and trying to solve some basic algorithm problems with it. In many cases, I want to read lines from stdin, perform some transformation on each line and return a vector of resulting items. One way I did this was like this:
// Fully working Rust code
let my_values: Vec<u32> = stdin
.lock()
.lines()
.filter_map(Result::ok)
.map(|line| line.parse::<u32>())
.filter_map(Result::ok)
.map(|x|x*2) // For example
.collect();
This works but of course silently ignores any errors that may occur. Now what I woud like to do is something along the lines of:
// Pseudo-ish code
let my_values: Result<Vec<u32>, X> = stdin
.lock()
.lines() // Can cause std::io::Error
.map(|line| line.parse::<u32>()) // Can cause std::num::ParseIntError
.map(|x| x*2)
.collect();
Where X is some kind of error type that I can match on afterwards. Preferably I want to perform the whole operation on one line at a time and immediately discard the string data after it has been parsed to an int.
I think I need to create some kind of Enum type to hold the various possible errors, possibly like this:
#[derive(Debug)]
enum InputError {
Io(std::io::Error),
Parse(std::num::ParseIntError),
}
However, I don't quite understand how to put everything together to make it clean and avoid having to explicitly match and cast everywhere. Also, is there some way to automatically create these enum error types or do I have to explicilty enumerate them every time I do this?
You're on the right track.
The way I'd approach this is by using the enum you've defined,
then add implementations of From for the error types you're interested in.
That will allow you to use the ? operator on your maps to get the kind of behaviour you want.
#[derive(Debug)]
enum MyError {
IOError(std::io::Error),
ParseIntError(std::num::ParseIntError),
}
impl From<std::io::Error> for MyError {
fn from(e:std::io::Error) -> MyError {
return MyError::IOError(e)
}
}
impl From<std::num::ParseIntError> for MyError {
fn from(e:std::num::ParseIntError) -> MyError {
return MyError::ParseIntError(e)
}
}
Then you can implement the actual transform as either
let my_values: Vec<_> = stdin
.lock()
.lines()
.map(|line| -> Result<u32,MyError> { Ok(line?.parse::<u32>()?*2) } )
.collect();
which will give you one entry for each input, like: {Ok(x), Err(MyError(x)), Ok(x)}.
or you can do:
let my_values: Result<Vec<_>,MyError> = stdin
.lock()
.lines()
.map(|line| -> Result<u32,MyError> { Ok(line?.parse::<u32>()?*2) } )
.collect();
Which will give you either Err(MyError(...)) or Ok([1,2,3])
Note that you can further reduce some of the error boilerplate by using an error handling crate like snafu, but in this case it's not too much.

Object oriented design patterns for error checking

I have written the following function that reads the contents of a text file and panic!s if an error is encountered.
fn get_file_contents(name: String) -> Result<String, io::Error> {
let mut f = try!(File::open(name));
let mut contents = String::new();
try!(f.read_to_string(&mut contents));
Ok(contents)
}
And the contents are extracted from the Result using:
let file_contents = match get_file_contents(file_name) {
Ok(contents) => contents,
Err(err) => panic!("{}", err)
};
I am now trying to reimplement this in an object oriented manner using structures and implementations. I created the following structure:
struct FileReader {
file_name: String,
file_contents: String,
}
and implemented the following methods:
impl FileReader {
fn new(fname: &str) -> FileReader {
FileReader {
file_name: fname.to_string(),
file_contents: String::new(),
}
}
fn get_file_contents(&mut self) {
let mut f = match File::open(&self.file_name) {
Ok(file) => file,
Err(err) => panic!("{}", err)
};
match f.read_to_string(&mut self.file_contents) {
Ok(size) => size,
Err(err) => panic!("{}", err)
};
}
}
In the OO approach, I haven't used the try! macro as I don't want the method to return any value. Is my OO implementation of get_file_contents a typical way of achieving this functionality? If not, can you please suggest an alternative way?
In the OO approach, I haven't used the try! macro as I don't want the method to return any value.
It's unclear why you think that "object oriented" means "doesn't return a value". If an error can occur, the code should indicate that.
Many languages have the equivalent of exceptions — out of band values that are thrown (also known as "returned") from a function or method. Note that this means that these languages allow for two disjoint types to be returned from a given function: the "normal" type and the "exceptional" type. That is a close equivalent for Rust's Result: Result<NormalType, ExceptionalType>.
Exceptional isn't a great term for this, as you should expect that opening a file should fail. There's an infinite number of ways that it could not work, but only a narrow subset of ways that it can succeed.
Panicking is closer to "kill the entire program / thread right now". Unlike C, you are forced to either deal with a problem, pass it back to the caller, or kill the program (panic).
If you would have thrown an exception in a language that supports them, use a Result. If you would have killed the program, or don't want to handle an error, use a panic.
If you want to panic in your particular case, use unwrap, or even better, expect:
fn get_file_contents(&mut self) {
let mut f = File::open(&self.file_name).expect("Couldn't open file");
f.read_to_string(&mut self.file_contents).expect("Couldn't read file");
}
seems kind of clunky to have to deal with the Result for each method.
Which is why the Error Handling section of The Rust Programming Language spends a good amount of time discussing the try! macro:
A cornerstone of error handling in Rust is the try! macro. The try! macro abstracts case analysis like combinators, but unlike combinators, it also abstracts control flow. Namely, it can abstract the early return pattern seen above.
(this makes more sense in context of the page)
I don't want my code to try and recover from the error (most likely caused by the file not being found) - I want it to print a useful error message and then die
Then by all means, panic. There's more succinct AND more detailed ways to do it (as shown above).

Is there a macro to convert an Error to Panic?

Is there a macro that will convert an error into a panic, similar to the try macro? Do I need to define my own?
For example I'd like to panic if a unit test can't open a file. My current workaround is this:
macro_rules! tryfail {
($expr:expr) => (match $expr {
result::Result::Ok(val) => val,
result::Result::Err(_) => panic!(stringify!($expr))
})
}
#[test]
fn foo() {
let c = tryfail!(File::open(...));
}
This is exactly what the methods Result::unwrap and Result::expect do.
I know you are asking for a macro, but I think your use case can be fulfilled with the unwrap method:
#[test]
fn foo() {
let c = File::open(...).unwrap();
// vs
let c = tryfail!(File::open(...));
}
Note that in code that is not test, it's more idiomatic to use expect.
If you really want a macro, you can write one using unwrap.

Reading from a processes stdout without placing it all in memory at once

I am trying to capture the output from an external tool which is run in a separate process. I would like to do so in a non-blocking way as the output is larger than memory. I saw How would you stream output from a Process in Rust? but I am not sure how to proceed. I have copied an example from here but this seems to capture output into a variable before proceeding. So far I have:
let path = "/Documents/Ruststuff/DB30_igh_badPE.bam";
let output = Command::new("samtools")
.arg("view")
.arg("-H")
.arg(path)
.stdin(Stdio::piped())
.stdout(Stdio::piped())
.spawn()
.unwrap_or_else(|e| panic!("failed {}", e));
let mut s = String::new();
match output.stdout.unwrap().read_to_string(&mut s) {
Err(why) => panic!("{}", Error::description(&why)),
Ok(_) => print!("{}", s),
}
Is it possible to loop over stdout from the child process instead of using the match? Something like:
for line in &output.stdout {}
You don't need non-blocking IO for what you want. You can use a buffered reader to loop over the lines of input. This assumes that you always need a full line, and that a full line isn't too much data:
use std::{
io::{BufRead, BufReader},
process::{Command, Stdio},
};
fn main() {
let mut child = Command::new("yes")
.stdout(Stdio::piped())
.spawn()
.expect("Unable to spawn program");
if let Some(stdout) = &mut child.stdout {
let lines = BufReader::new(stdout).lines().enumerate().take(10);
for (counter, line) in lines {
println!("{}, {:?}", counter, line);
}
}
}
ChildStdout implements Read for itself, but not for an immutable reference (&ChildStdout). Although we own the standard out, we don't want to consume it, so we need a reference of some kind. Read is implemented for a mutable reference to any other type that is itself Read, so we switch to a mutable reference. Then child needs to be mutable as well.

How can I get user input without receiving an "Unsed Variable" warning?

I'm taking a look at Rust and decided to build a small program that takes a user's input and prints it, but also want to do some math stuff with it for practice. Currently, this is how I am taking user input:
let mut number = String::new();
let input = io::stdin().read_line(&mut number)
.ok()
.expect("Failed to read line");
println!("You entered {}", number);
However, although I do get the correct input this way, Cargo gives me the following warning:
src/main.rs:10:9: 10:14 warning: unused variable: input, #[warn(unused_variables)] on by default
src/main.rs:10 let input = reader.read_line(&mut number)
If I were to just use the input variable, no matter what number I enter I would get a "2" in return when I print the number.
How can I avoid the warning? Is there another way for me to take input without creating 2 variable bindings?
You can simply not write the value to a variable. As long as the type of the value is not marked must_use, you can ignore the value.
let mut number = String::new();
io::stdin().read_line(&mut number)
.ok()
.expect("Failed to read line");
println!("You entered {}", number);
[commercial]
You can use the text_io crate for super short and readable input like
let i: i32 = read!()
let tup: (i32, String) = read!("{}, {}");
[/commercial]
It creates a warning because you are allocating space for a variable that is never used.
When faced with such warning you can either replace offending variable with _
let _ = io::stdin().read_line(&mut number) ...
or as ker noted just remove the variable altogether
io::stdin().read_line(&mut number)...
The _ will also work in other situation like parameters or in match clauses.
One additional option is to add #[allow(unused_variables)] in the module or crate and disable unused variable warnings. Although, I don't recommend it.