RustのAPIドキュメントにかかれている説明が全く意味が分からない
RustをインストールしてThe Bookのチュートリアルで疑問に思ったこと、「::」による呼び出しと「.」による呼び出しは何が違うんだということでした。おそらくはインスタンスのメソッド呼び出しと関数の呼び出しで手続きが違うのだろうと予想したので、戻り値がなにかAPIドキュメントを見てみました。ところが、そんなことはどこに書いてあるのかわからず、聞いたことのない用語の連続。こうして私はThe BookでRustを勉強するという、多くの人が選ぶ近道を断念しました。
私が代わりに選択したRust学習法
Boris Paskhaverという解説者のLearn to Code with RustというUdemyのチュートリアルを始めました。この解説者のチュートリアルはRspecのものを購入したことがあり、その丁寧でわかりやすい解説は既に経験があり、信頼できるチュートリアルだと推測できました。最も負担を感じたのは68時間というそのチュートリアルの長さです。しかし、Rustを丁寧に解説したらそのぐらい時間がかかるんじゃないか。The Bookで近道をしようとして挫折した私にはそう思えるようになりました。しかもこのチュートリアル、普通に買っても2600円という安さです。多くのオンラインチュートリアルが2〜3万という価格で販売されているのに対して、とても良心的な価格設定です。Udemyは定期的にセールをやりますが、例えばRoadToNextというチュートリアルのほうが、どのNext.jsのチュートリアルのほうがUdemyより参考になります。
またしてもぶつかった壁
それはシャドーイングって別に必用ない機能じゃないの、という事でした。急ぐのを止めた私は徹底的に調べて、Rustユーザーすらもその機能に疑問を抱いている人が少なからず居ることをしりました。その過程でRustの型変換についても調べました。Rustは暗黙には当たり前ですが行われません。ところが下のコードは可能です。
let parsed: i32 = "5".parse().unwrap();
これは文字列からInteger型への型変換ですが、Rustを知らない方はそう見えましたか?私はこれを見てRustの言語設計が酷いものだと思いました。もちろん、そんな酷い言語設計は使わなければ良いだけです。
上の表記は下のようにも書けます
let parsed = "10".parse::<i32>().unwrap();
ちなみに型変換が失敗するとunwrapはパニックを伝達してプログラムは停止します。ですから、外から来た値の型変換にこんなコードは書けません。
Swiftのほうが遥かに優れたコードになります
// 1. エラーの種類を定義
enum ParseError: Error {
case invalidInteger
}
// 2. 投げることができる(throws)関数を作成
func parseInteger(_ text: String) throws -> Int {
guard let value = Int(text) else {
throw ParseError.invalidInteger // 失敗したらエラーを投げる
}
return value
}
// 3. 実際に使うとき
do {
let turboParsed = try parseInteger("10")
print(turboParsed) // 10
} catch {
print("変換に失敗しました: \(error)")
}
Rustで平等にエラーハンドリングをつけた例
#[derive(Debug)]
enum MyError {
InvalidInteger,
}
// Result型を返す関数(Swiftの throws に相当)
fn parse_integer(text: &str) -> Result<i32, MyError> {
// .map_err で標準の ParseIntError を独自のエラー型に変換
text.parse::<i32>().map_err(|_| MyError::InvalidInteger)
}
fn main() {
let input = "10a"; // 無効な値の例
// match文でハンドリング(Swiftの do-catch に相当)
match parse_integer(input) {
Ok(value) => println!("変換成功: {}", value),
Err(e) => println!("エラーが発生しました: {:?}", e),
}
}
正直にいうと上の2つの例はRust by Exampleのコードで下の2つはAiに生成させたものです。どうやらRustは::を複数用途で用いるようなのです。先に私がThe Rustは出会ったのはモジュール内の関数呼び出しで、ここではGenericsの指定に使われいるようです。
Rustの言語仕様は非常に分かりづらいです
先にあげたシャドーイングの用途の不明瞭さに加えて、SwiftがInt(text)とtextを元に新しいInt型を生成していることが明示的なのに対して、Rustは::でGenericsを指定するという方法をとっており非常に読みづらいです。(正確にいうとIntは構造体でSwiftの構造体はクラスのように初期化メソッドを持てるのです。)正直、私はプログラミング言語自体が好きなので、色々な言語に挑戦してきた経験があります。その経験を踏まえて言うと、Genericsは別に難しい概念ではありませんが、Rustの使い方は読みづらいです。なぜかというと::を複数用途に用いるからです。Rustが難しいと言われるのは、こうした言語機能の複雑さがあるからです。正直、ここまで書いていて、また取り組むのが嫌になってきました。Rustでプログラミングをされる方は本当に尊敬しますが、私は1日で嫌いになりました。