iter()のfilter()内のクロージャー表記について(@Rust)

fn double_positives<'a>(numbers: &'a Vec) -> impl Iterator + 'a {
    numbers
        .iter()
        .filter(|x| x > &&0)
        .map(|x| x * 2)
}

fn main() {
    let singles = vec![-3, -2, 2, 3];
    let doubles = double_positives(&singles);
    assert_eq!(doubles.collect::<Vec<i32>>(), vec![4, 6]);
}

https://doc.rust-jp.rs/rust-by-example-ja/trait/impl_trait.html

なぜここで二重参照記号なのか?

.filter(|x| x > &&0)

Geminiに聞くと、

———————————

iter().filter(|x| x > &&0) における && の必要性

上記のコードは、Rust でイテレータを用いて要素をフィルタリングする例です。 && は論理積演算子であり、このコードにおいて 2 つの重要な役割を果たします。

1. 参照の借用:

Rust では、所有権と借用の概念に基づいてメモリ管理が行われます。 iter() メソッドは、コレクションの要素への 参照 を返すイテレータを生成します。 この参照は、& 演算子によって示されます。

一方、クロージャ |x| x > &&0 は、イテレータから渡される各要素に対して評価されます。 このクロージャ内でも要素への参照にアクセスする必要があるため、& 演算子を用いて 2 重参照 &&x とする必要があります。

2. 短絡評価:

&& 演算子は 短絡評価 される性質を持ちます。 これは、左側のオペランドが false である場合、右側のオペランドが評価されずに false を返すことを意味します。

この例では、x > 0 は常に true を返します。 つまり、たとえ &&0 が存在しなくても、結果は常に true となります。 しかし、&& を省略すると、コンパイラから警告が出力されます。 これは、意図せずに 0 との比較が行われていないことを確認するためです。

まとめ:

  • && は、イテレータから渡される要素への 2 重参照 を表し、クロージャ内で要素にアクセスできるようにします。
  • && は 短絡評価 されるため、パフォーマンス面での影響はありません。
  • 冗長に見えますが、意図せぬ 0 との比較を防ぎ、コードの可読性を向上させる役割を果たします。

補足

  • クロージャ内の変数 x は、イテレータから渡される各要素への 不変参照 です。 つまり、クロージャ内で x を変更することはできません。
  • コードをより簡潔に記述したい場合は、filter(|&x| x > 0) のように && を省略することもできます。 ただし、コンパイラからの警告が出力されることに注意が必要です。

———————————

上記で、filter(|&x| x > 0)は嘘(&&は形式的には論理積演算子だけど実は二重参照だからこの説明も微妙な説明)で、以下のようにしないとコンパイルは通りません。生成A.I.はそれらしい回答は出してきますが、正しいかどうかは別なので、解決の手掛かりをうるために使うのが現段階では正解だろうと思う。

.filter(|&x| x > &0)

あるいは、

.filter(|x| *x > &0)

あるいは、

.filter(|x| **x > 0)

あるいは、

.filter(|&x| *x > 0)

もしくは、

.filter(|&&x| x > 0)

ということで比較対象とするには参照記号や参照外しで、比較の『次元』を合わせないといけないということ。

https://doc.rust-lang.org/std/iter/trait.Iterator.html#method.filter

をみると、以下のような記述(クロージャーはfilter()に参照渡しだから二重参照になると)になってます

Because the closure passed to filter() takes a reference, and many iterators iterate over references, this leads to a possibly confusing situation, where the type of the closure is a double reference:

クロージャーに関するこの辺りの説明は、

https://zenn.dev/mebiusbox/books/22d4c1ed9b0003/viewer/64c6f3

「静的と動的に関わる記述」で詳しいと思う。

 

admin

カテゴリーRust