We will create a simple console program for Polish notation using Rust, which supports floating point numbers and negative numbers.
Console Input#
In Rust, obtaining console input is simple. The standard library env
provides a way for us to easily obtain console input.
let symbol:String = env::args()
// the nth input
.nth(1)
// simple error handling
.expect("Incomplete input");
let var_a:String = env::args().nth(2).expect("Incomplete input");
let var_b:String = env::args().nth(3).expect("Incomplete input");
println!("|{}|{}|{}|", symbol, var_a, var_b);
Now, let's run it and see.
cargo run -- 1 2 3
If everything is fine, you will see the following output in the console:
|1|2|3|
Here is the code:
use std::env;
// basic console input
fn obtain_var() {
// desired values
let symbol:String = env::args().nth(1).expect("Incomplete input");
let var_a:String = env::args().nth(2).expect("Incomplete input");
let var_b:String = env::args().nth(3).expect("Incomplete input");
println!("|{}|{}|{}|", symbol, var_a, var_b);
}
fn main() {
obtain_var();
}
But this is not enough. We also need to pass the obtained data to the calculation module, which involves type conversion.
Type Conversion#
To ensure the usability of our console application, we support decimal numbers and negative numbers. Numeric types need to be converted to f64
, which is a floating-point number.
// type conversion
fn change_type(var:String) -> f64 {
let var:f64 = match var.trim().parse() {
Ok(num) => num,
Err(_) => {
println!("Error: Invalid input");
// return Err
return 0.0;
}
};
// return the converted value
var
}
Next, we need to find a way to store our converted types. Here, I chose to use an array
for storage.
Current code:
// obtain console input
fn obtain_var() {
// desired values
let symbol:String = env::args().nth(1).expect("Incomplete input");
let var_a:String = env::args().nth(2).expect("Incomplete input");
let var_b:String = env::args().nth(3).expect("Incomplete input");
// convert types and store in an array for easy management
let number_var:[f64; 2] = [change_type(var_a), change_type(var_b)];
...
We also need a method to store the obtained values. Here, I chose to use a struct:
First, declare the struct at the beginning of the program:
...
// use a struct to store data
struct ComputeData {
symbol:String,
var_a:f64,
var_b:f64
}
...
Then, we set this function to return a struct for easy calling.
And create a struct to store the return values:
// obtain console input
fn obtain_var() -> ComputeData {
// desired values
let symbol:String = env::args().nth(1).expect("Incomplete input");
let var_a:String = env::args().nth(2).expect("Incomplete input");
let var_b:String = env::args().nth(3).expect("Incomplete input");
// convert types and store in an array for easy management
let number_var:[f64; 2] = [change_type(var_a), change_type(var_b)];
// store the data in a struct
let data = ComputeData {
symbol:String::from(symbol),
var_a:number_var[0],
var_b:number_var[1]
};
// function returns a struct
data
}
Now, we can write a calculation function.
Calculation#
Input data and return result:
// calculation
fn compute(symbol:String, var_a:f64, var_b:f64) -> f64
In theory, we only need to use a match
statement to check and return the value. However, in Rust, match
cannot directly check the String
type, so we need to convert it to &str
type.
Complete code:
// calculation
fn compute(symbol:String, var_a:f64, var_b:f64) -> f64 {
// use a reference to a slice to convert symbol to `&str`
let symbol:&str = &symbol.to_string()[..];
match symbol {
"+" => var_a + var_b,
"-" => var_a - var_b,
"*" => var_a * var_b,
"/" => var_a / var_b,
// error handling
_ => {
println!("Invalid calculation symbol");
// return `0.0` to indicate an error
0.0
}
}
}
Done!#
Now, let's write a main
function.
// improved readability
fn main() {
println!("{}",compute(
obtain_var().symbol,
obtain_var().var_a,
obtain_var().var_b
)
);
}
Running#
cargo run -- operator number1 number2
For example:
cargo run -- / 2 5
When running in the terminal, the result will be returned as 0.4
Complete Code#
use std::env;
// use a struct to store data
struct ComputeData {
symbol:String,
var_a:f64,
var_b:f64
}
fn main() {
println!("{}",compute(
obtain_var().symbol,
obtain_var().var_a,
obtain_var().var_b
)
);
}
// obtain console input
fn obtain_var() -> ComputeData {
// desired values
let symbol:String = env::args().nth(1).expect("Incomplete input");
let var_a:String = env::args().nth(2).expect("Incomplete input");
let var_b:String = env::args().nth(3).expect("Incomplete input");
// convert types and store in an array for easy management
let number_var:[f64; 2] = [change_type(var_a), change_type(var_b)];
// store the data in a struct
let data = ComputeData {
symbol:String::from(symbol),
var_a:number_var[0],
var_b:number_var[1]
};
// function returns a struct
data
}
// type conversion
fn change_type(var:String) -> f64 {
let var:f64 = match var.trim().parse() {
Ok(num) => num,
Err(_) => {
println!("Error: Invalid input");
// return Err
return 0.0;
}
};
// return the converted value
var
}
// calculation
fn compute(symbol:String, var_a:f64, var_b:f64) -> f64 {
// use a reference to a slice to convert symbol to `&str`
let symbol:&str = &symbol.to_string()[..];
match symbol {
"+" => var_a + var_b,
"-" => var_a - var_b,
"*" => var_a * var_b,
"/" => var_a / var_b,
// error handling
_ => {
println!("Invalid calculation symbol");
// return `0.0` to indicate an error
0.0
}
}
}
This is actually the first example I have done since learning Rust.