Rustつまづき集

Rustであっしまったとなったケースをメモしていきます。

Iteratorと所有権

最近所有権で悩まなくなってきたと思っていたのに久しぶりにやってしまった話。

このコード、どこがマズイでしょうか。

let hoge = vec![vec![1, 2, 3], vec![2, 4]];
let mapped = hoge.iter().map(|ns| {
    let c = 2;
    ns.iter().map(|n| c * n)
});

答えは

ns.iter().map(|n| c * n)

です。Iterator traitは基本的に遅延評価的なアレなので、外側のmapの返り値になっているMap構造体中では、c * nに対応する計算はなされていません。しかし、cの寿命は外側のmapに渡したクロージャ中で終わっているので、これでは内側のmapの計算ができません。それで叱られているわけですね。

怒られないようにするには正格評価にする

ns.iter().map(|n| c * n).collect::<Vec<_>>()

か、この簡単な場合ならmoveしてもよいでしょう。

ns.iter().map(move |n| c * n)

ただしmoveの場合、map中で他の変数を使っていたりすると面倒なことになります。

まあ当たり前なのですが、遅延評価と所有権が結びつくと一瞬混乱しますね。

追記 (2018/06/22)

またやってしまった…

pub modの順番とマクロ (2018/8/19)

現在(v1.28)のrustでは、マクロはそれまでに宣言されたものしか使えません。

hoge!(); // NG!
macro_rules! hoge { () => {{}}; }

子モジュールを宣言する際にも気を付ける必要があります。

// src/some_module/hoge.rs

macro_rules! hoge { () => {{1}}; }
// src/some_module/fuga.rs

#[test]
fn fuga() {
    assert_eq!(hoge!(), 1);
}
// src/some_module/mod.rs

// これならOK
pub mod hoge;
pub mod fuga;
// これはダメ
// pub mod fuga;
// pub mod hoge;

エラーメッセージが「#[macro_use]をつけろ」みたいに見当違いなことを言ってくるので結構はまりました。。。

Comments

comments powered by Disqus