HRTB (Higher-Rank Trait Bounds) によるライフタイム注釈について。

基本

通常版との差異

通常のライフタイム注釈
  • 関数、型、トレイトに付加される。

  • 実際のライフタイムの提供元はアイテムの操作元になる。
    (アイテムが関数なら呼出元、型やトレイトならインスタンスの生成元。)

HRTB によるライフタイム注釈
  • トレイト境界に付加される。

  • 実際のライフタイムの提供元はトレイト境界で制限した型の操作元になる。
    (トレイト境界がクロージャならその呼出元。)

書式

トレイト境界の左辺または右辺の先頭で for<'a> のように宣言する
(複数必要な場合、for<'a, 'b> のようにカンマで区切る)。

ライフタイム省略

HRTB によるライフタイム注釈がクロージャの引数や返値に適用されるパターンでは、関数の引数や返値における通常のライフタイム注釈と同様、ライフタイムの記載を省略できる (例: ライフタイム注釈が一つしかない場合など) 。

サンプル

クロージャ

以下では、関数 get_cmp_text でクロージャを扱うために HRTB が使用されている。

なお、このパターンはライフタイムが一つしかないため HRTB ごと省略可能。


use std::cmp::Ordering;

fn main() {
    let text = get_cmp_text(i32::cmp, 1, 2);
    assert_eq!(text, "<");
}

fn get_cmp_text<F>(cmp: F, x: i32, y: i32) -> String
where
    F: for<'a> Fn(&'a i32, &'a i32) -> Ordering
{
    match cmp(&x, &y) {
        Ordering::Less => "<",
        Ordering::Equal => "=",
        Ordering::Greater => ">",
    }.to_string()
}

GAT - 1

以下では、関数 sumGAT を用いたトレイトについて HRTB を使用している。


use std::slice::Iter;

fn main() {
    let vec = vec![1, 2, 3];
    assert_eq!(sum(vec), 6);
}

trait IntCollection {
    type IntStream<'a>: Iterator<Item = &'a i32> where Self: 'a;
    fn int_stream(&self) -> Self::IntStream<'_>;
}

impl IntCollection for Vec<i32> {
    type IntStream<'a> = Iter<'a, i32> where Self: 'a;
    fn int_stream(&self) -> Self::IntStream<'_> {
        self.iter()
    }
}

fn sum<T>(ints: T) -> i32
where
    T: for<'a> IntCollection<IntStream<'a> = Iter<'a, i32>>
{
    ints.int_stream().sum()
}

GAT - 2

以下では、関数 averageGAT を用いたトレイトについて HRTB を使用している。


use std::slice::Iter;

fn main() {
    let vec = vec![1, 2, 3];
    assert_eq!(average::<Vec<_>>(vec), 2.0);
}

trait IntCollection {
    type IntStream<'a>: Iterator<Item = &'a i32> where Self: 'a;
    fn int_stream(&self) -> Self::IntStream<'_>;
}

impl IntCollection for Vec<i32> {
    type IntStream<'a> = Iter<'a, i32> where Self: 'a;
    fn int_stream(&self) -> Self::IntStream<'_> {
        self.iter()
    }
}

fn average<T>(ints: T) -> f32
where
    T: IntCollection,
    for<'a> T::IntStream<'a>: ExactSizeIterator
{
    let sum = ints.int_stream().sum::<i32>();
    let len = ints.int_stream().len();
    sum as f32 / len as f32
}

制限

、HRTB には以下の制限がある。