We will create a simple console program for Polish notation using Rust, supporting floating-point numbers and negative numbers.
Console Input#
In Rust, obtaining console input is very simple. The standard library env
provides a way for us to easily obtain console input.
let symbol: String = env::args()
// Input position
.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|
The code at this point is:
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 that's not enough. We also need to pass the obtained data to the calculation module, which involves type conversion.
Type Conversion#
To ensure that our console application is somewhat usable, 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 0.0 to indicate an error
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 them 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 values we obtained. 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 access.
Also, create a struct to store the returned 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 them 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
.
Complete code:
// Calculation
fn compute(symbol: String, var_a: f64, var_b: f64) -> f64 {
// Convert symbol to `&str` using slice reference mode
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 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 them 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 0.0 to indicate an error
return 0.0;
}
};
// Return the converted value
var
}
// Calculation
fn compute(symbol: String, var_a: f64, var_b: f64) -> f64 {
// Convert symbol to `&str` using slice reference mode
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.