33

Rust: Begin the Journey With Code

 5 years ago
source link: https://www.tuicool.com/articles/hit/VJrY326
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

I hope you have already gone through the first article of our series, " RUST: Quick Start & Exploring Cargo " and are ready to workout your fingers and keys.

You might be thinking about the basic programming concepts of Rust as a language. With the help of an example, I will try to cover the following points (practically):

  • Mutable and immutable variables.
  • Standard input/output (console).
  • Functions.
  • Data type and conversion.
  • Loops: while, loop, for, continue, break.
  • Pattern matching.
  • Exception Handling.
  • Collections: Vector, HashMap, Iterator.

Let's solve a problem of aggregation, adding the values for the same category. I will try to demonstrate this with a practical example of stock and total stock holding.

Starting with the standard/console input-output and a dedicated function that returns the value as a tuple to print in the console. Copy-paste the below code in a new file, main.rs, and execute the following code:

use std::io;
fn main() {
    let (name, value) = read_io();
    println!("Stock name:{}, value:{}", name, value);
}

fn read_io() -> (String, i32) {
    println!("Please enter stock name:");
    let mut name = String::new();
    io::stdin()
        .read_line(&mut name)
        .expect("Failed to read name");

    let value: i32;
    loop {
        println!("Please enter stock value:");
        let mut value_str = String::new();
        io::stdin()
            .read_line(&mut value_str)
            .expect("Failed to read the value");

        value = match value_str.trim().parse() {
            Ok(num) => num,
            Err(_) => {
                println!("Value is not a number!!");
                continue;
            }
        };
        break;
    }
    (name.trim().to_string(), value)
}

This code snippet will take the input of the stock name and its value from the console and give that informattion as the output to verify everything working. So, let's now look at this code in detail.

Note: Rust follows snake_case instead of camelCase. rustc (Rust compiler) will give a warning if camelCase convention is used.

let (name, value) = read_io();
println!("Stock name: {}, value: {}", name, value);

In the above line (main function), first, we are calling a function, read_io() , which will return a tuple of a string (stock name) and an i32 number (stock value) as an immutable variable (by default, all variables are immutable in Rust). We will talk in detail about the data type and immutability in future posts related to them. In the next line, we are calling  the  println!   macro function to print the variables on the console.

use std::io;

Rust includes a few types in the scope of every program by default, but for the others not in the scope, we need to bring it into the scope explicitly with a use   statement. Using the above statement brings a library into the scope which provides us with useful features, to accept user input from the console.

Hope you are clear on the main() function code. Let's start with the  read_io() function:

fn read_io() -> (String, i32)

-> (String, i32)   defines that the function read_io()   will return a tuple of two variables. The first one will be a string data type and the ]other one is   i32 i.e. a signed 32 bits numeric value to use unsigned. We can use u32 instead of i32 for more detail; we will discuss this in the data type article.

let mut name = String::new();
io::stdin()
    .read_line(&mut name)
    .expect("Failed to read name");

Here, we have used mut   before  name   to declare it as a mutable variable;  String::new()   calls a new method of the  String   library (the  :: operator is used to access the library functions — by default  String   is in the scope of every program).

The next line, stdin() , is a function of the I/O library and is called to read the user input from the console.  read_line()   needs a mutable String variable to write the line input for the variable, which is the reason to create the name name as mutable ( &   indicates the reference of the variable). The  expect() function is used to handle the failure case if anything happens in reading the line and prints the message written in it as a parameter (Failed to read name) to the console.

In the next part, we have used a loop to read the value ( i32 ) as the input from the console is always a string. So to ensure the proper numeric value is used, we need to explicitly parse it and check the value to be numeric before going further.

value = match value_str.trim().parse() {
    Ok(num) => num,
    Err(_) => {
        println!("Value is not a number!!");
        continue;
    }
};
break;

Here, the   parse()   function is used to convert String to i32, this function can create a panic situationif the value is not a number. In Rust, this is dealt with using a match and a block which has ok(num)   and  Err(_)   functions to handle the situations (exceptions) accordingly. Now, we will continue to execute the loop code until we get a numeric value.

Note:The string value read contains  \n , we need to trim it and parse it with  to_string() before returning it, as it is a reference instead of a string value.

Let's move to the actual code (download code GitHub) and discuss how to store the inputs using collections (HashMap and Vector) and calculate the total valuation for each stock.

let mut stock_sum_map: HashMap<String, Vec> = HashMap::new();

Here, we are creating a mutable HashMap of string keys (stock names) and values, Vec , for the list of values of each stock(key).

I am skipping the check_io_flag()   functionality for you to understand it, as it much similar to  read_io() .

stock_sum_map.entry(name).or_insert(Vec::new()).push(value);

In the above code, we are using the entry(_)   method, which returns an enum called Entry   that represents a value that might or might not exist. Next, we have called the  or_insert(_)   method on  Entry ,   which will return a mutable reference to the value for the key if that key exists, and, if not, it inserts the parameter as the new value for the key and returns a mutable reference to the new value.

push(value) is a method of the Vector library which will add the value into the vector reference ( mutable ) returned by the previous method ( or_insert ).

for (name, vector) in stock_sum_map {

In the above code snippet, after reading values from the console input and storing it in the map, we are using a for each loop over the map to fetch each (key, value) pair from the reference.

let total_value: i32 = vector.iter().sum();
println!("Stock name: {}, total Value: {}", name, total_value);

Now, with the reference to the Vector containing the values of the stock (key), we can directly aggregate it in Rust using the sum()   method over the iterator ( .iter().sum() ).

In the last line, we have printed the total value of each stock with stock to the console.

You can download the code from the GitHub repo ( link ), and execute the code as explained in the first post.

Reference: The Rust Programming Language


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK