Rust Any Part 3: Finally we have Upcasts
文章介绍了 Rust 中的 As-Any Hack 技术及其在 Rust 1.86 中的修复。该技术用于实现超特征的向上转型和向下转型功能。修复后无需再依赖旧 hack,开发者可直接使用 `dyn Any` 进行类型转换。 2025-3-27 00:0:0 Author: lucumr.pocoo.org(查看原文) 阅读量:0 收藏

written on March 27, 2025

Three years ago I shared the As-Any Hack on this blog. That hack is a way to get upcasting to supertraits working on stable Rust. To refresh your memory, the goal was to make something like this work:

#[derive(Debug)]
struct AnyBox(Box<dyn DebugAny>);

trait DebugAny: Any + Debug {}

impl<T: Any + Debug + 'static> DebugAny for T {}

The problem? Even though DebugAny inherits from Any, Rust wouldn't let you use methods from Any on a dyn DebugAny. So while you could call DebugAny methods just fine, trying to use downcast_ref from Any (the reason to use Any in the first place) would fail:

fn main() {
    let any_box = AnyBox(Box::new(42i32));
    dbg!(any_box.0.downcast_ref::<i32>());  // Compile error
}

The same would happen if we tried to cast it into an &dyn Any? A compile error again:

fn main() {
    let any_box = AnyBox(Box::new(42i32));
    let any = &*any_box.0 as &dyn Any;
    dbg!(any.downcast_ref::<i32>());
}

But there is good news! As of Rust 1.86, this is finally fixed. The cast now works:

[src/main.rs:14:5] any.downcast_ref::<i32>() = Some(
    42,
)

At the time of writing, this fix is in the beta channel, but stable release is just around the corner. That means a lot of old hacks can finally be retired. At least once your MSRV moves up.

Thank you so much to everyone who worked on this to make it work!


For completeness' sake here is the extension map from the original block post cleaned up so that it does not need the as-any hack:

use std::any::{Any, TypeId};
use std::cell::{Ref, RefCell, RefMut};
use std::collections::HashMap;
use std::fmt::Debug;

trait DebugAny: Any + Debug {}
impl<T: Any + Debug + 'static> DebugAny for T {}

#[derive(Default, Debug)]
pub struct Extensions {
    map: RefCell<HashMap<TypeId, Box<dyn DebugAny>>>,
}

impl Extensions {
    pub fn insert<T: Debug + 'static>(&self, value: T) {
        self.map
            .borrow_mut()
            .insert(TypeId::of::<T>(), Box::new(value));
    }

    pub fn get<T: Default + Debug + 'static>(&self) -> Ref<'_, T> {
        self.ensure::<T>();
        Ref::map(self.map.borrow(), |m| {
            m.get(&TypeId::of::<T>())
                .and_then(|b| (&**b as &dyn Any).downcast_ref())
                .unwrap()
        })
    }

    pub fn get_mut<T: Default + Debug + 'static>(&self) -> RefMut<'_, T> {
        self.ensure::<T>();
        RefMut::map(self.map.borrow_mut(), |m| {
            m.get_mut(&TypeId::of::<T>())
                .and_then(|b| ((&mut **b) as &mut dyn Any).downcast_mut())
                .unwrap()
        })
    }

    fn ensure<T: Default + Debug + 'static>(&self) {
        if self.map.borrow().get(&TypeId::of::<T>()).is_none() {
            self.insert(T::default());
        }
    }
}

This entry was tagged rust


文章来源: http://lucumr.pocoo.org/2025/03/27/any-upcast/
如有侵权请联系:admin#unsafe.sh