数当てゲームを読み進む

前回は、Rustの数当てゲームのソースコード(途中版)を読みながら、標準ライブラリ、変数(不変、可変)、関数、型に関連付けられた関数などを見ていった。たった数行読んだだけだが、これだけでも多くの概念が使われていることがわかったと思う。

数当てゲームソースコード

use std::io;

fn main() {

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

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

let mut guess = String::new();

io::stdin()

.read_line(&mut guess)

.expect("読み込み失敗");

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

}

今回はさらに次の行へ進み、標準入力から文字列データとして入力を取得する方法を見ていく。

標準入力から文字列の読み込み

数当てゲームのソースコードでは、次の処理で標準入力にユーザーが入力する文字列データを取得している。

標準入力からユーザーの入力したデータを読み取る処理

io::stdin()

.read_line(&mut guess)

.expect("読み込み失敗");

より正確には、次の処理が標準入力からのデータ読み込み処理になっている。

より正確にはこの部分

io::stdin()

.read_line(&mut guess)

「io::stdin()」という表記は、「標準ライブラリのioモジュールにあるstdio()関数を呼び出す」という意味になる。ファイルの先頭で「use std::io;」と書いてあるので、io::stdin()のように書くことができる。この指定がなければ「std::io::stdin()」と書く必要がある。

では、実際にstdio()関数がどういったものか、ソースコードを見てみよう。該当する関数はrust/src/libstd/io/stdio.rsで作成されている。該当部分を抜粋すると次のようになっている(本稿執筆時点)。stdin()関数を読むと、stdin()関数は現在のプロセスに結び付けられた標準入力を操作するためのハンドラを返す関数であることがわかる。

std::io::stdin()のソースコード

/// Constructs a new handle to the standard input of the current process.

///

/// Each handle returned is a reference to a shared global buffer whose access

/// is synchronized via a mutex. If you need more explicit control over

/// locking, see the [`Stdin::lock`] method.

///

/// [`Stdin::lock`]: struct.Stdin.html#method.lock

...略...

#[stable(feature = "rust1", since = "1.0.0")]

pub fn stdin() -> Stdin {

static INSTANCE: Lazy = Lazy::new();

return Stdin {

inner: unsafe { INSTANCE.get(stdin_init).expect("cannot access stdin during shutdown") },

};

fn stdin_init() -> Arc {

// This must not reentrantly access `INSTANCE`

let stdin = match stdin_raw() {

Ok(stdin) => Maybe::Real(stdin),

_ => Maybe::Fake,

};

Arc::new(Mutex::new(BufReader::with_capacity(stdio::STDIN_BUF_SIZE, stdin)))

}

}

rust/src/libstd/io/stdio.rsに記載されているstdin()関数

io::stdin()を呼ぶと、Stdinが返ってくるようだ。Stdinも同じファイルで実装されており、該当部分を抜き出すと次のようになっていることがわかる。

Stdinの宣言部分

/// A handle to the standard input stream of a process.

///

/// Each handle is a shared reference to a global buffer of input data to this

/// process. A handle can be `lock`'d to gain full access to [`BufRead`] methods

/// (e.g., `.lines()`). Reads to this handle are otherwise locked with respect

/// to other reads.

///

/// This handle implements the `Read` trait, but beware that concurrent reads

/// of `Stdin` must be executed with care.

///

/// Created by the [`io::stdin`] method.

///

/// [`io::stdin`]: fn.stdin.html

/// [`BufRead`]: trait.BufRead.html

///

...略...

#[stable(feature = "rust1", since = "1.0.0")]

pub struct Stdin {

inner: Arc,

}

標準入力は現在のプロセスに結び付けられたものとして準備されており、標準入力から得られたデータを保持するグローバルなバッファに対する共有参照という形でStdinが実装されているようだ、といったこともこのソースコードからなんとなく見えてくる。

参照

次のソースコードで、stdin()によって得られたStdinに実装されているread_line()という関数が呼ばれていることになる。

より正確にはこの部分

io::stdin()

.read_line(&mut guess)

Stdinの実装も先程のファイルに記載されており、次のようになっている。

Stdinの実装部分

impl Stdin {

...略...

pub fn lock(&self) -> StdinLock