无需宏为 Trait Objects 实现 Any

std::any::Any 是 Rust 在运行时进行类型擦除和转换的工具,所有 'static 类型都实现了 Any,因此装箱为 Box<dyn Any> 后可以借助 Any::type_id() 获取 TypeId,还可以通过 dyn Any 的 downcast_*() 等方法再转换回具体类型。 然而问题也出在 dyn Any 的 downcast_*() 方法。这些方法是 dyn Any 的方法,而不是 Any trait 中的方法,所以其他任何 dyn Trait 都不会拥有这些方法,即使有 Trait: Any。另一方面,由于 trait upcasting 直到最近才成为稳定特性,且还没进入 stable 版本,所以依赖语言支持的 trait upcasting 来转换为 dyn Any 对于较早版本的项目并不合适。 因此,本文则通过纯 Rust 语法来扩展 trait object,以实现 Any 的所有功能。这些实现都可以在 better-as-any 找到。 现有解决方案 downcast downcast crate 通过宏来生成代码,直接为指定的 dyn Trait 添加 is()、downcast_*() 方法。这种做法在最终效果上与 dyn Any 完全一样,但是需要使用 impl_downcast!() 宏来实现。我个人则偏好能使用语言特性实现就不使用宏。...

March 17, 2025 · 4 min · oosquare

anyerr 上下文中的类型体操

在对 Rust 错误处理的思考和 anyerr一文中,我介绍了 anyerr 这一个错误处理库,其可以携带上下文信息,且储存上下文的数据结构是可定制的。本文则聚焦 anyerr 是如何实现这样的特性的。 上下文的核心特性 基本结构和表示 在设计之前,首先我们要明确需求。什么样的上下文数据结构是我们所需要的?携带上下文是为了能够记录某些变量所保存的值,我们需要记录变量的名称和其中的值。上下文可以有很多种类,但所有的上下文都可以表示为一个键值映射表。 所以以下是我们对一个上下文储存的基本特性的定义: pub trait AbstractContext: Default + Debug + Send + Sync + 'static { type Key; type Value; type Entry: Entry<Key = Self::Key, Value = Self::Value>; type Iter<'a>: Iter<'a, Entry = Self::Entry> where Self: 'a; fn iter(&self) -> Self::Iter<'_>; } 这样的一个 trait 定义仅仅规定了一个上下文的键值对类型和迭代其中元素的方法,却没有插入或者其他查询的方法。这是因为一个上下文不一定需要真的携带有信息,如果不需要上下文,那么一个不带有任何信息的上下文就可以非常好地适用于这种场景,这也是为什么这个 trait 叫做 AbstractContext。anyerr 针对这样的情况有特殊的优化,这些后面再说。 上下文的元素 AbstractContext::Entry 规定了上下文中每一个元素的类型,其应当实现 Entry trait。以下则是 Entry 的定义: pub trait Entry: Debug + Send + Sync + 'static { type Key: Borrow<Self::KeyBorrowed> + Debug + Send + Sync + 'static; type KeyBorrowed: Debug + Display + Eq + Hash + ?...

February 27, 2025 · 6 min · oosquare