コレクションを動的借用する場合の定石について。

概要

コレクションの小技

コレクションからイテレータを取得して impl Iterator<Item = &T> のように抽象化すると、コレクションそのものを扱うよりも汎用性が高くなる。

動的借用時の問題点

上記の技法はそのままでは動的借用されたコレクションでは使えない。なぜなら、イテレータは一般的にコレクションから借用されるのでなく生成される。そのため、コレクション経由のイテレータの借用は不可能である。つまり、動的借用されるコレクションからイテレータを動的借用する事もできない。

解決策

コレクションに AsIterator トレイトを導入し、そこからイテレータを生成するとよい。

サンプル

以下では、動的借用の例として RefCell<T> を使用している。
(RwLock<T>Mutex<T> でも同様の解決方法が適用可能。)


use std::cell::{Ref, RefCell};

fn main() {
    let var = SomeType::new("test".to_string(), vec![1, 2, 3]);
    assert_eq!(var.id(), "test");
    assert!(var.values().iter().eq(vec![1, 2, 3].iter()));
}

struct SomeType(RefCell<SomeInfo>);

impl SomeType {
    fn new(id: String, values: Vec<i32>) -> Self {
        Self(RefCell::new(SomeInfo { id, values }))
    }

    fn id(&self) -> String {
        self.0.borrow().id.to_string()
    }

    fn values(&self) -> Ref<'_, impl AsIterator<Item = i32>> {
        Ref::map(self.0.borrow(), |x| &x.values)
    }
}

struct SomeInfo {
    id: String,
    values: Vec<i32>,
}

trait AsIterator {
    type Item;
    fn iter(&self) -> impl Iterator<Item = &Self::Item>;
}

impl<T> AsIterator for Vec<T> {
    type Item = T;
    fn iter(&self) -> impl Iterator<Item = &Self::Item> {
        <[T]>::iter(&self)
    }
}