という記事がトレンドに乗ってたので・・
Rustなのか?Rustなのかい?
って思って開いてみた所・・・
ok, err := foo()
ってやった場合と
val, ok := bar["key"]
みたいにやった時に何がokなんだかよくわかんなくなる方の言語でした。
いや、使い方が悪いんでしょうけど。
最初、if val, ok := bar["key"]; ok{ ...
みたいなコード見て混乱した覚えがあります。keyがあれば値が入るし、keyがないとfalseが返ってくる。ちょっと気持ち悪い。
ってごめんなさい。Goも好きです。disってないです。
Rustの場合
で、自分的に「例外を投げない」でピンと来た方がこちら。
Rust初心者なのですが生意気にすいません。
Rustも例外のない言語です。
仕様的にはGoに近くて多値返却ではあるんですが、その多値を受け取る型が用意されています。
失敗する可能性のある関数だとすんなりとString型とかを返却する事はなくて、そのStringをなんらかの形
でwrapして返す。
なので受け取ったStringを使いたかったらそのなんらかの形
から取り出して使うしかない。
Rustのエラーハンドリング
https://doc.rust-jp.rs/the-rust-programming-language-ja/1.6/book/error-handling.html
Result型
こういう感じの列挙型です
pub enum Result<T, E> {
Ok(T),
Err(E),
}
例えばファイルを開くstd::fs::File::open
は開こうと思ったファイルの存在やら権限やらによっては失敗する関数です。
シンプルに開くだけの例だとこんな感じです。開こうとするとResultのenumが返却されるので、OkのパターンとErrのパターンに対応できる。
use std::fs;
fn main() {
//returns `io::Result<File>`
match fs::File::open("foo.txt") {
Ok(_f) => println!("opened foo.txt "),
Err(_e) => panic!("couldn't open foo.txt "),
}
}
Option型
Someってなんやねん。判りづらい。。
正常な値が存在すればstd::option::Option::Some
を介して取り出せるって考えてればいいんだと思います。
pub enum Option<T> {
None,
Some(T),
}
例えば、こんな感じでゼロ除算するとNoneの値が返って、それ向けに書いた処理が実行されます。
正常に割り切れたものは当然正常な処理が実行される。
// An integer division that doesn't `panic!`
fn checked_division(dividend: i32, divisor: i32) -> Option<i32> {
if divisor == 0 {
// Failure is represented as the `None` variant
None
} else {
// Result is wrapped in a `Some` variant
Some(dividend / divisor)
}
}
// This function handles a division that may not succeed
fn try_division(dividend: i32, divisor: i32) {
// `Option` values can be pattern matched, just like other enums
match checked_division(dividend, divisor) {
None => println!("{} / {} failed!", dividend, divisor),
Some(quotient) => {
println!("{} / {} = {}", dividend, divisor, quotient)
},
}
}
fn main() {
try_division(4, 2);
try_division(1, 0);
}
//https://doc.rust-lang.org/rust-by-example/std/option.html
実行するとこんな感じに。
$ cargo run
Finished dev [unoptimized + debuginfo] target(s) in 0.00s
Running `target/debug/rusttest`
4 / 2 = 2
1 / 0 failed!
こんな感じでいちいち値がwrapされる。
ちなみにこういう感じでwrapされててもunwrap
とか使うと簡単に取り出す事はできる。
アンチパターンになりがちだけど、あっさり書いてもいい場所には使える。
expect
でそのままpanicにしちゃったりもできる。
https://doc.rust-lang.org/std/option/enum.Option.html#method.unwrap
let x = Some("air");
println!("{}", x.unwrap());
std::collections::HashMap.get()
HashMapでgetしたときもOptionが返ってきます。
unwrap()
しちゃうとこんな感じ。
use std::collections::HashMap;
fn main(){
let mut map = HashMap::new();
map.insert("foo", "bar");
println!("value is {}",map.get(&"foo").unwrap());
}
Rustややこしい
Rustの 3大ややこしい のうちの一つがこのエラー処理なのではなかろうか。
他の言語でぜんぜん馴染みないし。
でもプログラマがエラーを強制的に意識して書かなくてはいけないってのは安全なコードにつながるし、Rustの思想なんだと思う。
補足
例外はないけど、Dropトレイトっていうデストラクタはあるみたいです。try~catch的な事もできるかもしれないです。原則として後始末用なんだと思いますが。