Default トレイトでジェネリクス型の既定値を参照する方法について。

失敗例

以下では、内部アイテムが外側アイテムのジェネリクス型を使えない制限に引っかかる。


use std::any::Any;

fn main() {
    assert_eq!(default::<i32>(), &0);
    assert_eq!(default::<bool>(), &false);
}

fn default<T>() -> &'static T
where
    T: Default + Any + Sync + Send,
{
    static VAL: T = Default::default();
    &VAL
}

error[E0401]: can't use generic parameters from outer item
  --> src\main.rs:12:17
   |
 8 | fn default<T>() -> &'static T
   |            - type parameter from outer item
... 
12 |     static VAL: T = T::default();
   |            ---  ^ use of generic parameter from outer item
   |            |
   |            generic parameter used in this inner static item
   |
   = note: nested items are independent from their parent item for everything except for privacy and name resolution
   = note: a `static` is a separate item from the item that contains it

サンプル

以下では、ハッシュを経由して制限を回避している (少し遅い)。


use std::any::{Any, TypeId};
use std::collections::HashMap;
use std::sync::{LazyLock, RwLock};

fn main() {
    assert_eq!(default::<i32>(), &0);
    assert_eq!(default::<bool>(), &false);
}

fn default<T>() -> &'static T
where
    T: Default + Any + Sync + Send,
{
    static MAP: LazyLock<RwLock<Map>> = LazyLock::new(Default::default);
    type Map = HashMap<TypeId, Box<dyn Any + Sync + Send>>;

    let map = MAP.read().unwrap();
    if let Some(val) = map.get(&TypeId::of::<T>()) {
        let val = val.downcast_ref::<T>().unwrap();
        return unsafe { &*(val as *const _) };
    }

    drop(map);
    let mut map = MAP.write().unwrap();
    let bx = Box::new(T::default());
    let entry = map.entry(TypeId::of::<T>()).insert_entry(bx);
    let val = entry.get().downcast_ref::<T>().unwrap();
    unsafe { &*(val as *const _) }
}