Expand description
用于动态类型或类型反射的实用工具。
Any
和 TypeId
Any
本身可以用来得到一个 TypeId
,当用作 trait 对象时,它有更多的特性。
作为 &dyn Any
(借用的 trait 对象),它具有 is
和 downcast_ref
方法,以测试所包含的值是否为给定类型,并对该类型的内部值进行引用。
作为 &mut dyn Any
,还有 downcast_mut
方法,用于获取内部值的变量引用。
Box<dyn Any>
添加了 downcast
方法,该方法尝试转换为 Box<T>
。
有关完整的详细信息,请参见 Box
文档。
请注意,&dyn Any
仅限于测试值是否为指定的具体类型,而不能用于测试某个类型是否实现 trait。
智能指针和 dyn Any
将 Any
用作 trait 对象时要记住的一种行为,尤其是对于 Box<dyn Any>
或 Arc<dyn Any>
之类的类型,只需在值上调用 .type_id()
即可生成容器的 TypeId
,而不是底层 trait 对象。
可以通过将智能指针转换为 &dyn Any
来避免,这将返回对象的 TypeId
。
例如:
use std::any::{Any, TypeId};
let boxed: Box<dyn Any> = Box::new(3_i32);
// 您更可能希望这样做:
let actual_id = (&*boxed).type_id();
// ... 比这个:
let boxed_id = boxed.type_id();
assert_eq!(actual_id, TypeId::of::<i32>());
assert_eq!(boxed_id, TypeId::of::<Box<dyn Any>>());
RunExamples
考虑一下我们要注销传递给函数的值的情况。 我们知道我们正在实现的值实现了 Debug,但是我们不知道它的具体类型。我们要对某些类型进行特殊处理:在这种情况下,应先打印 String 值的长度,然后再打印它们的值。 我们在编译时不知道我们值的具体类型,因此我们需要使用运行时反射。
use std::fmt::Debug;
use std::any::Any;
// 用于实现 Debug 的任何类型的 Logger 函数。
fn log<T: Any + Debug>(value: &T) {
let value_any = value as &dyn Any;
// 尝试将我们的值转换为 `String`。
// 如果成功,我们希望输出 String 的长度及其值。
// 如果不是,那就是另一种类型:只需将其打印出来即可。
match value_any.downcast_ref::<String>() {
Some(as_string) => {
println!("String ({}): {}", as_string.len(), as_string);
}
None => {
println!("{value:?}");
}
}
}
// 该函数要先注销其参数,然后再使用它。
fn do_work<T: Any + Debug>(value: &T) {
log(value);
// ...做一些其他的工作
}
fn main() {
let my_string = "Hello World".to_string();
do_work(&my_string);
let my_i8: i8 = 100;
do_work(&my_i8);
}
RunProvider
和 Demand
Provider
和相关的 API 支持泛型、类型驱动的数据访问,以及实现者提供此类数据的机制。
该接口的关键部分是用于提供数据的对象的 Provider
trait,以及用于从实现 Provider
的对象请求数据的 request_value
和 request_ref
函数。
通常,最终用户不应该直接调用 request_*
,它们是中间实现者用来实现面向用户的接口的辅助函数。
这纯粹是为了人体工程学,这里没有安全问题; 中间实现者通常可以支持方法而不是 free 函数,并使用更具体的名称。
通常,数据提供者是扩展 Provider
的 trait 的 trait 对象。用户将通过指定数据的类型从 trait 对象请求数据。
数据流
- 用户请求特定类型的对象,该对象委托给
request_value
或request_ref
request_*
创建一个Demand
对象并将其传递给Provider::provide
Provider::provide
的数据提供者实现尝试使用Demand::provide_*
提供不同类型的值。如果类型与用户请求的类型匹配,则该值将存储在Demand
对象中。request_*
解包Demand
对象并将任何存储的值返回给用户。
Examples
use std::any::{Provider, Demand, request_ref};
// MyTrait 的定义,一个数据提供者。
trait MyTrait: Provider {
// ...
}
// `MyTrait` trait 对象的方法。
impl dyn MyTrait + '_ {
/// 获取对实现结构体的字段的引用。
pub fn get_context_by_ref<T: ?Sized + 'static>(&self) -> Option<&T> {
request_ref::<T>(self)
}
}
// `MyTrait` 和 `Provider` 的下游实现。
impl MyTrait for SomeConcreteType {
// ...
}
impl Provider for SomeConcreteType {
fn provide<'a>(&'a self, demand: &mut Demand<'a>) {
// 提供一个字符串引用。
// 我们可以在这里提供不同类型的多个值。
demand.provide_ref::<String>(&self.some_string);
}
}
// `MyTrait` 的下游用法。
fn use_my_trait(obj: &dyn MyTrait) {
// 向 obj 请求 &String。
let _ = obj.get_context_by_ref::<String>().unwrap();
}
Run在这个例子中,如果 use_my_trait
中 obj
的具体类型是 SomeConcreteType
,那么 get_context_by_ref
调用将返回一个引用给 &String
类型的 obj.some_string
。
Structs
- DemandExperimental用于按类型提供数据的帮助器对象。
TypeId
代表类型的全局唯一标识符。
Traits
- ProviderExperimentalTrait 由可以根据类型动态提供值的类型实现。
- 一个用来模拟动态类型的 trait。
Functions
- request_refExperimental从
Provider
请求引用。 - request_valueExperimental从
Provider
请求一个值。 - type_name_of_valExperimental以字符串切片的形式返回指向的值的类型的名称。 这与
type_name::<T>()
相同,但是可以在不容易获得变量类型的地方使用。 - 以字符串切片的形式返回类型的名称。