トレイト境界のfor
rustには公式入門書に載っていない深淵の構文がいくつかあります。
(2025年5月現在)その一つが、関数のトレイト境界に記載するfor文です。
// こんなん
F: for<'a> Fn(&'a str)
HRTB (Higher-Rank Trait Bounds) といいます。高階トレイト境界と訳すようです。
以下にその動作をライフタイムの基本的な知識がある前提でご説明します。
概要
このforは、単純にいえば『どれでも』という意味です。どんなライフタイムでも問題なく動く'aを想定した定義が、このHRTBです。
明示的に利用するタイミングとしては、『コールバック関数を定義する際、コールバック関数にライフタイムを指定する必要がある時』等です。(他の場合にも使いますが、ここでの使い方が理解できれば大体大丈夫です)
通常ライフタイムを関数等のジェネリクスとして指定する際、そのライフタイムの実体は呼び出し元の文脈に依存しますよね。
しかし、コールバック関数においてはコールバックを呼び出す関数内の文脈によってライフタイムが構成される可能性があるため、必ずしも定義だけで確定できません。
// これだとcall_with_strの中の文脈にあるライフタイムを指定できない
fn call_with_str<'a,F>(f: F)
where
F: Fn(&'a str)
{
let s = String::from("hello");
f(&s); // コンパイルエラー
}
本質的には、『関数内部の実装によって確定する=関数定義で確定しない』ライフタイムの定義をしたい訳です。
そこでこのfor文が活躍します。forとは、『これを満たすものすべて』の意味です。
コールバック関数に対してどのようなライフタイムであれ許容する実装を求める事で、コンパイルエラーを解消します。
// いけた
fn call_with_str<F>(f: F)
where
F: for<'a> Fn(&'a str)
{
let s = String::from("hello");
f(&s);
}
素晴らしい。コンパイルが通りました。
ただし、前記の例文ではライフタイムを全て省略しても糖衣としてコンパイルが通ります(!)。コールバックを利用している時も、特に指定なしでもなんとな〜くうまくいくことの方が多いわけですね。
前提として、HRTBは『覚えておくと使い方が増える』類の構文ではありません。いつの間にか使われている、の方が多いと思います。
ただ、様々〜なレアケースの中で、急にエラー原因としてコイツが現れることがあります。
StackOverFlowのやつ
稀なタイミングでは必要になるので、その際は抑えておく、というイメージかなと思います。
余談
ちなみにこちらの機能、公式リファレンスブックと共に、The Rustonomiconに記載があります。
RustnomiconというのはRust公式の珍しい機能解説ページです。
クトゥルフ神話に登場する架空の魔道書『Necronomicon』が由来であり、まさしく深淵の機能解説となっています...。