rust学习-any中的downcast和downcast_ref

背景

看rust官方文档,好奇Any和Go的Any是否是一回事,看到下文的一行代码,了解下它的功能

pub trait Any: 'static {
   
    // Required method
    fn type_id(&self) -> TypeId;
}

std::any 用于 dynamic typing 或者 type reflection
模拟动态类型的trait。

大多数类型都实现 Any。但是,任何包含非静态引用的类型都不会。

use std::any::Any;

fn main() {
   
	use std::any::{
   Any, TypeId};

	fn is_string(s: &dyn Any) -> bool {
   
		TypeId::of::<String>() == s.type_id()
	}

	assert_eq!(is_string(&0), false);
	assert_eq!(is_string(&"cookie monster".to_string()), true);
}

Any and TypeId

TypeId 代表一个全局唯一类型标识符
Any 本身可以用来获取 TypeId,并且当用作 Trait 对象时具有更多功能(used as a trait object)。

  • 作为 &dyn Any(借用的trait对象)
    它具有 is 和 downcast_ref 方法,用于测试包含的值是否属于给定类型(is of a given type),并获取对内部值的引用(get a reference to the inner value as a type)。
  • 作为 &mut dyn Any
    有 downcast_mut 方法,用于获取对内部值的可变引用(mutable reference)。 Box 添加向下转换方法,该方法尝试转换为 Box。

注意
&dyn Any 仅限于测试值是否属于指定的具体类型(alue is of a specified concrete type)
不能用于测试类型是否实现特征(a type implements a trait)。

Smart pointers 和 dyn Any

使用 Any 作为trait对象时要记住的一个行为,尤其是使用 Box 或 Arc 等类型时,只需对值调用 .type_id() 将生成容器的 TypeId(比如Box、Arc) ,而不是底层trait对象(simply calling .type_id() on the value will produce the TypeId of the container, not the underlying trait object)。

可以通过将智能指针转换为 &dyn Any 来避免这种情况,这将返回对象的 TypeId。

use std::any::{
   Any, TypeId};

fn main() {
   
        let boxed: Box<dyn Any> = Box::new(3_i32);
        let boxed_any: &dyn Any = &Box::new(3_i32);

        // 对值调用 .type_id() 将生成容器的 TypeId
        let boxed_id = boxed.type_id();
        // 将智能指针转换为 &dyn Any 来避免这种情况,返回对象的 TypeId
        let boxed_any_id = boxed_any.type_id();
        // 但是你可能更期待这样
        let actual_id = (&*boxed).type_id();

        assert_eq!(boxed_id, TypeId::of::<Box<dyn Any>>()); // Box
        assert_eq!(boxed_any_id, TypeId::of::<Box<i32>>()); // Box
        assert_eq!(actual_id, TypeId::of::<i32>());         // i32
}

考虑一种情况,要打印(log out)传递给函数的值。已知道正在处理的值实现 Debug(the value we’re working on implements Debug),但不知道它的具体类型。
同时又希望对某些类型给予特殊处理:在这种情况下,先打印 String 值的长度,再打印它们的值(print out the length of String values prior to their value.)。在编译时不知道值的具体类型,因此我们需要使用运行时反射(runtime reflection)。

use std::fmt::Debug;
use std::any::Any;

// 对任何实现了Debug的类型进行Logger打印
fn log<T: Any + Debug>(value: &T) {
   
    // 一个值 value 进行类型转换,并将其转换为 &dyn Any 类型的引用。
    // 这种转换允许将一个具体类型的值视为实现了 Any trait 的类型,
    // 并且可以在运行时进行类型检查和转换
    // 
    // 【value_any视为一个实现了 Any trait 的类型】
    let value_any = value as &dyn Any;

    // 【将动态类型转换为具体类型】
    // downcast_ref 方法来尝试将 &dyn Any 引用转换为 String 类型的引用
    // 它会返回一个 Option,以便在转换失败时处理错误情况
    match value_any.downcast_ref::<String>() {
   
        Some(as_string) => {
   
            println!("String ({}): {}", as_string.len(), as_string)

你可能感兴趣的:(rust,rust,学习)