見出し画像

Rust学習 - 基本構文キャッチアップ#2(+α: みんなのRust学習の事情を知る)

基本構文キャッチアップその2。キャッチアップはこれで一区切りで、ブロックチェーン作りながら学ぶ流れに戻そう。

+αの部分は、ちょっと知りたかったので、このタイミングでいくつか調べることに。結果としては、一番良くまとまっていたブログを見つけた。参考にする部分を抜粋し次に活かすヤツ。

本題


Rustの公式リファレンスはとても充実している。すべてnoteに移すとキリがない。ザーッと確認はしたが、特に記憶したくなった部分をnoteに書くことにして、次に進みます。

アトリビュート

  • アトリビュートはモジュール、クレート、要素に対するメタデータ。いくつかのプログラムを拝読し、この構文は把握しなければならないと認識。他の言語にもよくあるアノテーション構文ではある。やったことは、定義済み(ビルトイン/built-in)アトリビュートが紹介されていたのを一読した。

以下はその備忘録だ。

// This function only gets compiled if the target OS is linux
// この関数はターゲットOSがLinuxの時のみコンパイルされる。
#[cfg(target_os = "linux")]
fn are_you_on_linux() {
    println!("You are running linux!");
}

// And this function only gets compiled if the target OS is *not* linux
// そしてこの関数はターゲットOSがLinux *ではない* ときのみコンパイルされる。
#[cfg(not(target_os = "linux"))]
fn are_you_on_linux() {
    println!("You are *not* running linux!");
}

fn main() {
    are_you_on_linux();

    println!("Are you sure?");
    if cfg!(target_os = "linux") {
        println!("Yes. It's definitely linux!");
    } else {
        println!("Yes. It's definitely *not* linux!");
    }
}



#[allow(non_camel_case_types)] 
type int8_t = i8; 

// Inner attribute applies to the entire function.
fn some_unused_variables() {
  #![allow(unused_variables)]
  let important = ();
  let variable = ();
  let be_used = ();
  let dont_care = ();
}

↑なるほど。自分でアトリビュートを定義することはできるのだろうか。どうやらできるようだ。「proc_macro_attribute」というアトリビュートを付与したパブリック関数を実装すると、その関数がカスタムアトリビュートとなり、ビルトインアトリビュートと同じように他の関数などに付与できるらしい。

クロージャ


関数の構文の1種らしい。外側の環境を捕捉した関数のこと。なるほど、よくわからない。

サンプルのコードを読んだ。つまり「クロージャ」を使ったコードとは、以下のようなコード。
※「func」がクロージャー。クロージャ「func」は「x」を捕捉(クロージャの外側の環境にある変数xを捕捉)した上で、この場合呼び出しに応じて101という値を返却していることになる)


fn main() {
   let x = 100;
   let func = |val:u32| { val + x };
   let res: u32 = func(1);
   
   println!("Hello, world with {}", res);
}

クロージャの特徴を抜粋↓。呼び出す側からすると通常の関数と何も変わらないヤツ。

  • 入力変数を囲むのに、()の代わりに||を用いる

  • クロージャの処理の本体を{}で加工お作法。ここは、関数と同じ。ただし、本体が単一の式の場合だけ、本体の区切り文字({})を省略でき、それ以外の場合は{}で囲うのは必須という取り扱い。

  • 外側の環境にある変数を捕捉することができます。

構文を理解する上ではすこし脱線する気がするが、クロージャーの代表的な使用例。stdライブラリ内のstd::iter::Iteratorらしい。使ってみた。

fn main() {
   let hoge = vec!(1,2,3);
   let fuga = hoge.iter().find(|&&val| { val >= 2});
   println!("{:?}", fuga);
}

↑なるほど。説明にあったのだが、yieldされた要素へのリファレンスは &&i32 となる とはいったいなんぞ?いろいろとわからない。

  • yieldするとは一体何か

  • 何起因でリファレンスは&&i32になるのか。

その謎を解明するべく我々はアマゾンの奥地へ向かった

しらべたが正確な情報源がわからなかった。断片的なコードは出てきた。いい感じの情報はぱっと見つからない。強いて言うのなら↓か。

  • yieldが 「save the state of closure and return」 ってことは、イテレータの各要素を一時「yield=収容」する文字通りの意味でいいのか?

  • 「&&i32」の意味は?説明が見つからない。そのまま解釈すると参照の参照/ポインターのポインター的な意味

    • 推測で言えば「もとの可変長配列(vec!)の要素」をイテレータは参照する(1つ目の&の意味)。さらにイテレータのfindメソッドは個別の要素をyieldし、クロージャの引数にはその参照を与える(2つ目の&)。たぶんそうなのだろう。

Match構文

Rust言語では、Match構文というのがあるようだ。よく出てくる。
C言語などのswitch構文相当でもあり、式として使うこともできるのか。なるほど。そして、すべての値をカバーしないとコンパイル時に怒られることがわかった。


let myNumber = 1;

match myNumber {
    // 単一の値とのマッチをチェック
    1 => println!("One!"),
    // いくつかの値とのマッチをチェック
    2 | 3 | 5 | 7 | 11 => println!("This is a prime"),
    // TODO ^ Try adding 13 to the list of prime values
    // TODO ^ 素数のリストに 13 を加えてみましょう
    // Match an inclusive range
    // 特定の範囲の値とのマッチをチェック
    13..=19 => println!("A teen"),
    // Handle the rest of cases
    // その他の場合の処理
    _ => println!("Ain't special"),
}
let boolean = true;

let binary = match boolean {
  // マッチは全ての可能な値をカバーしなくてはならない
  false => 0,
  true => 1,
};

println!("{} -> {}", boolean, binary);

Result列挙型


前回列挙体を理解した時、Option型を知り得た。Resultは親戚みたいなものか?つまり、
失敗する可能性のある関数の返り値として、失敗の理由をつけて返したい場合標準ライブラリの中に定義されている「Result」という列挙体を使うらしい。こちらもプログラムを拝読し、あまりにも頻出だったのでここでとりあげておく。OkとErrという2つの列挙子が定義されているので覚えよう。

試し打ちしたコードをそっとおいておく。書いて覚えるのはよいことだ。
Ok(100)の後ろにセミコロンをつけるなとコンパイラに怒られた。セミコロンをつけるべきポイントが少しわからなくなった。

#[derive(Debug)]
enum HogeErrorType {
    
    Feeling,
    Temperature,
}

type HogeResult = Result<u32, HogeErrorType>;

fn check_hoge(val : u32) -> HogeResult {
    
    if val == 100 {
    
        Ok(100)
    }else if val == 200 {
        
        Err(HogeErrorType::Feeling)
    }else {
        
        Err(HogeErrorType::Temperature)
    }
}

fn main() {
    
    let res = check_hoge(200);
    println!("{:?}", res);
}

よし。こんな感じだろう。あとは実践で理解していこう。


本題+α


Rustの学習方法をもっといい感じにしたい。公式サイトを読んでいくのもよいけれど。他の人がどんなふうにRustを学習しているのか。

なんとそれらしいブログがあるじゃないか。
少し拝借して中身を確認。


色々あるな。
この非公式のデザインパターン、どの程度有効なのだろう。
不定期、かつ、部分的に学習をしていくか。

コマンドラインツールを作りながら学ぶのは、オーソドックスだけどよいな。テスト関数の書き方も教えてくれるようだし、これを通してみるのは良さそうだ。



よし。
次は、ブロックチェーン作りながら学ぶ。2回目のチャレンジだ。ちゃんと理解した上でコードを書けるか確認していく。

また次回!

この記事が気に入ったらサポートをしてみませんか?