Rust雑入門1

〜rustcと戯れる〜

お品書き

  • Rustのインストール
  • hello world!
  • なんちゃってcatコマンド作成
    • をやりながら、Rustの基礎知識を得る

インストール

Download

multirust

開発機にはこっちがおすすめ

multirust update stable するだけで

最新の安定版がインストールされる

hello world!

hello world!

fn main() {
    println!("Hello World!");
}

hello world!

fn main() {
    println!("Hello World!");
}

fnは関数(メソッド)定義用の予約語

mainはCと同じ役割

hello world!

fn main() {
    println!("Hello World!");
}

println!はマクロ。Rustではマクロには!が付く。

hello world!

fn main() {
    println!("Hello World!");
}

hello.rsと名前を付けて保存し、

rustc hello.rs を実行すると、helloが生成されるので、./hello で実行すると、Hello World!と出力される。

簡単!

rustc

  • たぶん普段の開発ではrustcは使わずcargoを使う
  • 言語仕様を把握するための簡単なプログラムを書く程度ならrustcで十分という時がある
  • そういう時のためにrustcを使ってみた
  • 今回は単一ファイルしか扱わないのでrustcで十分(外部のライブラリも使わない)

なんちゃってcat

仕様

なんちゃってcat

  • 引数に渡されたファイルの中身を表示
  • 標準入力は完全無視
  • オプションも全無視

なんちゃってcat

情報収集

なんちゃってcat

とりあえず、自身を出力するコードを作成

// show_myself.rs
use std::fs::File;
use std::io::prelude::*;

fn main() {
    let mut file = File::open("show_myself.rs").unwrap();
    let mut content = String::new();
    file.read_to_string(&mut content);
    println!("{}", content);
}

それぞれの行を見ていこう

なんちゃってcat

// show_myself.rs
use std::fs::File;
use std::io::prelude::*;

fn main() {
    let mut file = File::open("show_myself.rs").unwrap();
    let mut content = String::new();
    file.read_to_string(&mut content);
    println!("{}", content);
}

useでstd::fs::File::open("")とフルパスで書くところを、File::open("")と書けるようにしている。

なんちゃってcat

// show_myself.rs
use std::fs::File;
use std::io::prelude::*;

fn main() {
    let mut file = File::open("show_myself.rs").unwrap();
    let mut content = String::new();
    file.read_to_string(&mut content);
    println!("{}", content);
}

read_to_stringはstd::io::Read(trait)に含まれるで必要(依存)

これがないと、read_to_stringというメソッドがないというエラーが発生する。

なんちゃってcat

// show_myself.rs
use std::fs::File;
use std::io::prelude::*;

fn main() {
    let mut file = File::open("show_myself.rs").unwrap();
    let mut content = String::new();
    file.read_to_string(&mut content);
    println!("{}", content);
}

ファイルを開く。

unwrapについては後ほど詳しく説明。

mutは変更可能にする宣言。

Rustはimmutable by default。一度中身を入れた変数は二度と変更できない。

let mut x = 1; とすると、後から変更できる

なんちゃってcat

// show_myself.rs
use std::fs::File;
use std::io::prelude::*;

fn main() {
    let mut file = File::open("show_myself.rs").unwrap();
    let mut content = String::new();
    file.read_to_string(&mut content);
    println!("{}", content);
}

空の文字列を用意。

mutで後から変更可能にしている。

これにファイルの中身を入れる。

なんちゃってcat

// show_myself.rs
use std::fs::File;
use std::io::prelude::*;

fn main() {
    let mut file = File::open("show_myself.rs").unwrap();
    let mut content = String::new();
    file.read_to_string(&mut content);
    println!("{}", content);
}

ファイルの中身を全て文字列としてcontentに代入。

&mutは書き込み可能でborrowしている。

borrowすることでownership(スコープの終了と同時に変数などを破棄する権限)は移らず、次の行でも引き続きcontentが使える。

なんちゃってcat

// show_myself.rs
use std::fs::File;
use std::io::prelude::*;

fn main() {
    let mut file = File::open("show_myself.rs").unwrap();
    let mut content = String::new();
    file.read_to_string(&mut content);
    println!("{}", content);
}

contentを表示

println!の使い方はRust by Exampleが参考になる。

なんちゃってcat

// show_myself.rs
use std::fs::File;
use std::io::prelude::*;

fn main() {
    let mut file = File::open("show_myself.rs").unwrap();
    let mut content = String::new();
    file.read_to_string(&mut content);
    println!("{}", content);
}

いきなりですが、ここでQuestion

何か大事なことを忘れています。

気づいたかしら?

なんちゃってcat

// show_myself.rs
use std::fs::File;
use std::io::prelude::*;

fn main() {
    let mut file = File::open("show_myself.rs").unwrap();
    let mut content = String::new();
    file.read_to_string(&mut content);
    println!("{}", content);
}

正解はFile::close();でも書かなくてOK。

Rustではスコープの終了と同時にFileが閉じられる。(Drop Traitを参照。drop(file)で手動で閉じることもできる)

詳しくはHow to close a file?あたりを読んでね!

ちなみにFile::closeというメソッドはない。

unwrap

Rustでよく見かける

unwrap


File::open("show_myself.rs")
  • File::openはResult<File>を返す
  • FileはResultというラップに包まれているのでunwrapする
  • なぜそんなことが必要なのか?
  • ファイルが存在しないなどの理由によりErrorを返すケースがありえる
  • エラーが起こりえる場合には、ResultやOptionを使うのでunwrapを多く見かける
  • 詳しくはError Handling参照

unwrap


File::open("show_myself.rs").unwrap();
  • ちなみに、存在しないファイルを指定してunwrapするとpanicを起こしクラッシュする
  • なので、このプログラムは製品コードとして到底受け入れられるものではない
  • 普通はpattern matchなどで制御する
  • unwrap_or(T)などもある

unwrap

use std::error::Error;
use std::fs::File;
use std::io::prelude::*;
use std::process::exit;

fn main() {
    let mut file = match File::open("show_myself.rs") {
        Ok(file) => file,
        Err(why) => {
            println!("Error occured: {}", Error::description(&why));
            exit(1);
        },
    };
    //...
}

exitする雑な例

再なんちゃってcat

再なんちゃってcat

引数の処理

cat file1.txt file2.txt ...

のように、複数指定できるようにしたい

再なんちゃってcat

cat a b c
# => ["./cat", "a", "b", "c"]
use std::env;

fn main() {
    let args: Vec<String> = env::args().collect();
    println!("{:?}", args);
}

args[1..n]を処理すれば良さそう

再なんちゃってcat

use std::fs::File;
use std::io::prelude::*;
use std::env;

fn main() {
    let args: Vec<String> = env::args().collect();
    match args.len() {
        0 | 1 => help(),
        _ => cat(args)
    }
}

args.len()が0か1(引数なし)なら、ヘルプを表示

それ以外はcat関数を実行

再なんちゃってcat

static USAGE: &'static str = "
Usage: cat <file> [<file>...]

Output file content to stdout
";

fn help() {
    // なんとなく定数を使ってみた
    print!("{}", USAGE);
}

ヘルプの実装

再なんちゃってcat

fn cat(pathes: Vec<String>) {
    for path in pathes.into_iter().skip(1) {
         let mut file = match File::open(&path) {
            Err(_) => panic!("couldn't open {}", path),
            Ok(file) => file, 
         };
         let mut content = String::new();
         match file.read_to_string(&mut content) {
            Err(_) => panic!("couldn't read {}", path),
            Ok(_) => print!("{}", content),
         };
    }
}

catの雑な実装

再なんちゃってcat

fn cat(pathes: Vec<String>) {
    for path in pathes.into_iter().skip(1) {
         let mut file = match File::open(&path) {
            Err(_) => panic!("couldn't open {}", path),
            Ok(file) => file, 
         };
         let mut content = String::new();
         match file.read_to_string(&mut content) {
            Err(_) => panic!("couldn't read {}", path),
            Ok(_) => print!("{}", content),
         };
    }
}

特に再利用する予定がなかったので

owershipをぶんどる引数にした

再なんちゃってcat

fn cat(pathes: Vec<String>) {
    for path in pathes.into_iter().skip(1) {
         let mut file = match File::open(&path) {
            Err(_) => panic!("couldn't open {}", path),
            Ok(file) => file, 
         };
         let mut content = String::new();
         match file.read_to_string(&mut content) {
            Err(_) => panic!("couldn't read {}", path),
            Ok(_) => print!("{}", content),
         };
    }
}

iteratorにしてskip(1)することで、args[1]以降が処理されるので、catの引数だけを処理している

再なんちゃってcat

panic!はクラッシュさせるマクロ

あとは解説済みだと思うので省略

ここではサボっているが、panic!はmain以外の関数では起こさず、Result<Error>を返すべし

fn cat(pathes: Vec<String>) {
    for path in pathes.into_iter().skip(1) {
         let mut file = match File::open(&path) {
            Err(_) => panic!("couldn't open {}", path),
            Ok(file) => file, 
         };
         let mut content = String::new();
         match file.read_to_string(&mut content) {
            Err(_) => panic!("couldn't read {}", path),
            Ok(_) => print!("{}", content),
         };
    }
}

次回予告

  • Cargoを使います(3rd party libraryを使うので)
  • Docoptを使ってもう少しイケてる感じにします
  • structが出てきます
  • その他ネタ募集中
  • catをガチでRustで実装する例はこちら
    • 本勉強会はここまでやりません
    • 行番号を表示したら終了し、次のネタに(concurrencyとか)移ります

Rust雑入門1

By dopin

Rust雑入門1

rustcを使ってみる

  • 1,198