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
- Rust by Exampleで
- ファイル(file)の扱い方と
- 引数(arg)の取り方を
- を検索
- std::fsとstd::envを使うらしい..
- ok ok
情報収集
なんちゃって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