This time we will learn match, shadowing, type conversion

The source code that was taken up last time in the series is as follows. Through this source code, I learned about “standard library”, “variable”, “type function”, “standard input”, “reference”, “panic processing”, “crate”, and “trait” provided by Rust.

Source code that I have read up to the last time

use std::io;
use rand::Rng;

fn main() {
    println!("数当てゲーム");

    let secret_number = rand::thread_rng().gen_range(1, 101);

    println!("シークレットナンバーはこれ: {}", secret_number);

    println!("どの数だとおもう? = ");

    let mut guess = String::new();

    io::stdin()
        .read_line(&mut guess)
        .expect("読み込み失敗");

    println!("入力値: {}", guess);
}

Rust is sometimes classified as a “multi-paradigm programming language.” This is because it incorporates several distinctive programming language concepts such as procedural, object-oriented, and functional. One reason we can learn a lot from short source code is that Rust is a multi-paradigm programming language and has many characteristics.

This time, while reading The Book, we will learn about “match flow control operator”, “type conversion”, and “shadowing”. In particular, the match flow control operator is written in a Rust-specific way, so I think it may be new to users of other major programming languages.

match flow control operator

First, let’s rewrite the source code of the number guessing game as follows.

match Number guessing game with flow control operator

use rand::Rng;
use std::cmp::Ordering;
use std::io;

fn main() {
    println!("数当てゲーム");

    let secret_number = rand::thread_rng().gen_range(1, 101);

    println!("シークレットナンバーはこれ: {}", secret_number);

    println!("どの数だとおもう? = ");

    let mut guess = String::new();

    io::stdin()
        .read_line(&mut guess)
        .expect("読み込み失敗");

    println!("入力値: {}", guess);

    match guess.cmp(&secret_number) {
        Ordering::Less => println!("小さすぎです!"),
        Ordering::Greater => println!("大きすぎです!"),
        Ordering::Equal => println!("そのとおり!"),
    }
}

The following new line has been added to the use statement.

Newly added use statement

use std::cmp::Ordering;

I added std :: cmp :: Ordering to the scope in the use statement to use this type in number guessing games. Ordering is an enumeration and its constituents are Less, Greator, and Equal. I think there is a definition near rust / blob / master / library / core / src / cmp.rs, so you can see that only these two are available (the source code changes the path frequently, so it’s different. It may be a URL. In that case, search for pub enum Ordering to find the relevant source code).

Definition part of Ordering type

pub enum Ordering {
    /// An ordering where a compared value is less than another.
    #[stable(feature = "rust1", since = "1.0.0")]
    Less = -1,
    /// An ordering where a compared value is equal to another.
    #[stable(feature = "rust1", since = "1.0.0")]
    Equal = 0,
    /// An ordering where a compared value is greater than another.
    #[stable(feature = "rust1", since = "1.0.0")]
    Greater = 1,
}
  • https://github.com/rust-lang/rust/blob/master/library/core/src/cmp.rs

    https://github.com/rust-lang/rust/blob/master/library/core/src/cmp.rs

It’s easy to think of the match flow control operator as something like the switch and case control syntaxes in other major programming languages. The main difference is its strength or wide range of support.

In the previous source code, I added the following match flow control operator.

Part using the newly added match flow control operator

    match guess.cmp(&secret_number) {
        Ordering::Less => println!("小さすぎです!"),
        Ordering::Greater => println!("大きすぎです!"),
        Ordering::Equal => println!("そのとおり!"),
    }

The comparison is done in the guess.cmp () part, but this function returns the Ordering type. The process to be executed is separated by match according to the value. It’s easy to swallow if you think of it as something like switch control syntax or case control syntax. I think it’s easier to understand than listing if else. The match flow control operator is a powerful feature. Detailed usage will appear in another chapter of The Book, so let’s deepen your understanding there.

Type conversion

When I build the source code, I get the following error.

Cannot build in this state

% cargo build
   Compiling guessing_game v0.1.0 (/Users/daichi/Documents/rust_testbed/guessing_game)
error[E0308]: mismatched types
  → src/main.rs:22:21
      match guess.cmp(&secret_number) {
                        ^^^^^^^^^^^^^^ expected struct `std::string::String`, found integer
   = note: expected reference `&std::string::String`
              found reference `&{integer}`

error: aborting due to previous error

For more information about this error, try `rustc --explain E0308`.
error: could not compile `guessing_game`.

To learn more, run the command again with --verbose.
% 

The variable guess is a string. Therefore, the character string is also required for the comparison target in guess.cmp (). However, in reality, an integer is specified, which is a build error.

To avoid this error, prepare guess as an integer instead of a string. The following source code is the source code that can be built.

Source code rewritten so that it can be built

use rand::Rng;
use std::cmp::Ordering;
use std::io;

fn main() {
    println!("数当てゲーム");

    let secret_number = rand::thread_rng().gen_range(1, 101);

    println!("シークレットナンバーはこれ: {}", secret_number);

    println!("どの数だとおもう? = ");

    let mut guess = String::new();

    io::stdin()
        .read_line(&mut guess)
        .expect("読み込み失敗");

    let guess: u32 = guess
        .trim()
        .parse()
        .expect("数を入力してください!");

    println!("入力値: {}", guess);

    match guess.cmp(&secret_number) {
        Ordering::Less => println!("小さすぎです!"),
        Ordering::Greater => println!("大きすぎです!"),
        Ordering::Equal => println!("そのとおり!"),
    }
}

The following parts have been added so that they can be built.

    let guess: u32 = guess
        .trim()
        .parse()
        .expect("数を入力してください!");

First, trim () removes whitespace. Rust provides trim () as a function to remove characters with the White_Space property in Unicode. White space, tab, and line feed characters correspond to this. In the sample code, io :: stdin (). read_line () is used to get the character string from the standard input, and the line feed code is included at the end of the character string. trim () is used to remove it.

Next, parse () is used to convert it to a u32 type integer. If a character string that cannot be converted is entered, the message specified by expect () is output and an error occurs.

Rust handles types statically. Type inconsistencies can be detected during the build phase. The type of programming language that casts automatically is convenient, but it can be difficult to notice when a problem occurs. Static is more coding, but it can detect type inconsistencies at build time, making it easier to eliminate problems in advance.

Shadowing

By the way, you may have noticed that the sample source code mentioned earlier is incomprehensible. First, a variable called guess is defined. This is the same as before.

String variable guess

    let mut guess = String::new();

After getting the string data from the standard input, the variable guess is created again as follows. It’s likely to be a build error, but it’s not an error.

Integer variable guess

    let guess: u32 = guess
        .trim()
        .parse()
        .expect("数を入力してください!");

In Rust, creating a variable with the same variable name with let is called “shadowing”. Shadowing is often used for the following purposes:

  • Do not increase variable names unnecessarily
  • Change variable type

For example, if you try to do the same thing in another programming language, you might have a variable with a different name, such as guess_str for a string or guess_int for an integer. With Rust shadowing, you can change the type while keeping the name the same. Converting types without incrementing variables, this is a characteristic use of Rust.

You can change the contents of a variable variable, but you cannot change the type. However, if you take an approach that prepares ambiguous types, it will be difficult to thoroughly check at build time. Shadowing can be said to be a mechanism that ensures ease of use while maintaining the rigidity of a programming language.

Number guessing game

The book’s number guessing game is also nearing completion. You should be able to build your current source code as follows:

Number guessing game that can be built

% cargo build
   Compiling guessing_game v0.1.0 (/Users/daichi/Documents/rust_testbed/guessing_game)
    Finished dev [unoptimized + debuginfo] target(s) in 0.30s
%

Next, each execution sample is posted. It can be confirmed that each is operating as intended.

Number guessing game execution example: Bingo

% cargo run
    Finished dev [unoptimized + debuginfo] target(s) in 0.01s
     Running `target/debug/guessing_game`
数当てゲーム
シークレットナンバーはこれ: 46
どの数だとおもう? = 
46
入力値: 46
そのとおり!
% 

Number guessing game execution example: Large value

% cargo run
    Finished dev [unoptimized + debuginfo] target(s) in 0.02s
     Running `target/debug/guessing_game`
数当てゲーム
シークレットナンバーはこれ: 58
どの数だとおもう? = 
100
入力値: 100
大きすぎです!
% 

Number guessing game execution example: Small value

% cargo run
    Finished dev [unoptimized + debuginfo] target(s) in 0.01s
     Running `target/debug/guessing_game`
数当てゲーム
シークレットナンバーはこれ: 45
どの数だとおもう? = 
1
入力値: 1
小さすぎです!
% 

Number guessing game execution example: Case where non-numbers are input

% cargo run
    Finished dev [unoptimized + debuginfo] target(s) in 0.02s
     Running `target/debug/guessing_game`
数当てゲーム
シークレットナンバーはこれ: 4
どの数だとおもう? = 
a
thread 'main' panicked at '数を入力してください!: ParseIntError { kind: InvalidDigit }', src/main.rs:20:22
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
% 

The number guessing game, which is short but teaches many things, is nearing completion. Even though it is such a source code, the functions and backend that can be read from it are very large. This area is both fun and difficult for Rust.

However, if you have programming experience, you may already feel that “this is hard, but it feels good and has been put into reality.” Let’s follow the number guessing game a little more.

Reference material

  • The Rust Programming Language – The Rust Programming Language