前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
社区首页 >专栏 >Rust Arc指针类型转换:dyn转换为具体类型

Rust Arc指针类型转换:dyn转换为具体类型

作者头像
灯珑LoGin
发布于 2023-10-18 02:49:42
发布于 2023-10-18 02:49:42
66000
代码可运行
举报
文章被收录于专栏:龙进的专栏龙进的专栏
运行总次数:0
代码可运行

前言

在写代码的时候,经常通过dyn关键字+Arc指针来实现多态。但是,有时候会遇到这样一个需求:我们想获取具体类型的Arc指针。比如,结构体A实现了trait Base,想要把Arc<dyn Base>转换为Arc<A>.

为了实现这种转换,有开发者写了一个库,叫做downcast-rs,以支持上述要求。但是,这个库还要求了对象一定要被Box包裹,也就是,指针形式是Arc<Box<dyn Base>>转换为Arc<Box<A>>.由于DragonOS里面,文件系统的Inode指针原本就是Arc<dyn IndexNode>这种类型的,没有被Box包裹,因此不能直接使用downcast-rs这个库。

方法可行性验证

因此,在这里我想了一种方法(包含unsafe的)来实现这个需求。大概原理就是,判断Arc<dyn Base>的类型是否为A,如果是的话,就把Arc<dyn Base>转换为裸指针,再通过Arc::from_raw方法,得到Arc<A>。

我认为,既然Arc<dyn Base>是可以由Arc<A>直接转换而来,那么,只要我能确定Arc<dyn Base>的真实类型就是A,那应该就能转换回去。指向的目标的内存布局应该是一样的。

因此,我写了核心的代码:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
impl dyn Base {
    fn as_any_arc(self: Arc<Self>) -> Arc<dyn Any> {
        return self;
    }
    fn downcast_arc<T: Base>(self: Arc<Self>) -> Option<Arc<T>> {
        
        let x = self.as_any_arc();

        if x.is::<T>() {
            
            // into_raw不会改变引用计数
            let p = Arc::into_raw(x);
            
            let new = unsafe { Arc::from_raw(p as *const T) };
            return Some(new);
        }
        println!("x.is not <{}>", std::any::type_name::<T>());
        return None;
    }
}

上述代码先把Arc<Self>转换为Arc<dyn Any>,然后,判断Self的类型是否为转换目标T。如果是的话,则调用Arc::into_raw和Arc::from_raw,完成Arc的类型转换。

下面是完整的测试程序,以及它的输出。可以看到,转换转换之后,能够正常调用具体类型结构体的成员函数。下面的测试程序中还跟踪了Arc指针的引用计数,可以发现,引用计数也是正常的。因此我“大胆猜测”,这里的转换代码没有问题。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
#![feature(trait_upcasting)]
use std::{any::Any, fmt::Debug, sync::Arc};

trait Base: Any + Send + Sync + Debug {
    fn get_name(&self) -> String;
}

impl dyn Base {
    fn as_any_arc(self: Arc<Self>) -> Arc<dyn Any> {
        return self;
    }
    fn downcast_arc<T: Base>(self: Arc<Self>) -> Option<Arc<T>> {
        println!("count before: {}", Arc::strong_count(&self));
        let x = self.as_any_arc();
        let y = x.clone();

        if x.is::<T>() {
            println!("x.is::<{}>", std::any::type_name::<T>());
            println!("count in1: {}", Arc::strong_count(&y));
            // into_raw不会改变引用计数
            let p = Arc::into_raw(x);
            println!("count in2: {}", Arc::strong_count(&y));
            let new = unsafe { Arc::from_raw(p as *const T) };
            println!("count after: {}", Arc::strong_count(&new));
            return Some(new);
        }
        println!("x.is not <{}>", std::any::type_name::<T>());
        return None;
    }
}

#[derive(Debug)]
struct A {
    name: String,
}

impl Base for A {
    fn get_name(&self) -> String {
        self.name.clone()
    }
}

impl A {
    fn say_a(&self) {
        println!("say_a");
    }
}

#[derive(Debug)]
struct B {
    name: String,
}

impl Base for B {
    fn get_name(&self) -> String {
        self.name.clone()
    }
}

impl B {
    fn say_b(&self) {
        println!("say_a");
    }
}

fn main() {
    let a: Arc<A> = Arc::new(A {
        name: "A".to_string(),
    });
    let b: Arc<B> = Arc::new(B {
        name: "B".to_string(),
    });

    let x: Arc<dyn Base> = a as Arc<dyn Base>;
    let y: Arc<dyn Base> = b as Arc<dyn Base>;
    assert!(x.clone().downcast_arc::<A>().is_some());
    assert!(x.clone().downcast_arc::<B>().is_none());
    assert!(y.clone().downcast_arc::<A>().is_none());
    assert!(y.clone().downcast_arc::<B>().is_some());

    let a1: Arc<A> = x.downcast_arc::<A>().expect("x is not A");
    println!("a1: {:?}", a1);
    a1.say_a();

    let b1: Arc<B> = y.downcast_arc::<B>().expect("y is not B");
    println!("b1: {:?}", b1);
    b1.say_b();
}

输出:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
count before: 2
x.is::<downcast::A>
count in1: 3
count in2: 3
count after: 3
count before: 2
x.is not <downcast::B>
count before: 2
x.is not <downcast::A>
count before: 2
x.is::<downcast::B>
count in1: 3
count in2: 3
count after: 3
count before: 1
x.is::<downcast::A>
count in1: 2
count in2: 2
count after: 2
a1: A { name: "A" }
say_a
count before: 1
x.is::<downcast::B>
count in1: 2
count in2: 2
count after: 2
b1: B { name: "B" }
say_a

优雅地将它封装成trait

如果我们有多个trait都要实现以上功能,为每个trait单独写一套代码的话,就太不优雅了。因此,在这一步,我们将上述功能封装成一个叫做DowncastArc的trait:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
/// @brief 将Arc<dyn xxx>转换为Arc<具体类型>的trait
trait DowncastArc: Any + Send + Sync {
    /// 请在具体类型中实现这个函数,返回self
    fn as_any_arc(self: Arc<Self>) -> Arc<dyn Any>;

    /// @brief 将Arc<dyn xxx>转换为Arc<具体类型>
    /// 
    /// 如果Arc<dyn xxx>是Arc<具体类型>,则返回Some(Arc<具体类型>),否则返回None
    /// 
    /// @param self Arc<dyn xxx>
    fn downcast_arc<T: Any + Send + Sync>(self: Arc<Self>) -> Option<Arc<T>> {
        let x: Arc<dyn Any> = self.as_any_arc();
        if x.is::<T>() {
            // into_raw不会改变引用计数
            let p = Arc::into_raw(x);
            let new = unsafe { Arc::from_raw(p as *const T) };
            return Some(new);
        }
        return None;
    }
}

这里我们用到了一个unstable的特性,因此需要在lib.rs中添加:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
#![feature(trait_upcasting)]

使用这个trait

接着,我们可以这样使用它,为Base这个trait实现DowncastArc:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
impl DowncastArc for dyn Base {
    fn as_any_arc(self: Arc<Self>) -> Arc<dyn Any> {
        return self;
    }
}

请注意,这要求Base这个trait实现了Any + Send + Sync这三个trait.

测试代码与上文的main()函数的相同。

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2023年4月16日2,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
rust智能指针
智能指针虽然也号称指针,但是它是一个复杂的家伙:通过比引用更复杂的数据结构,包含比引用更多的信息,例如元数据,当前长度,最大可用长度等。引用和智能指针的另一个不同在于前者仅仅是借用了数据,而后者往往可以拥有它们指向的数据,然后再为其它人提供服务。智能指针往往是基于结构体实现,它与我们自定义的结构体最大的区别在于它实现了 Deref 和 Drop 特征:
zy010101
2023/05/09
1.1K0
Rust for Linux 源码导读 | Ref 引用计数容器
2022 年,我们很可能会看到 Linux 内核中的实验性 Rust 编程语言支持成为主流。2021.12.6 早上发出了更新的补丁,介绍了在内核中处理 Rust 的初始支持和基础设施。
张汉东
2022/01/23
1.3K0
《Rust避坑式入门》第1章:挖数据竞争大坑的滥用可变性
赵可菲是一名Java程序员,一直在维护一个有十多年历史的老旧系统。这个系统即将被淘汰,代码质量也很差,每次上线都会出现很多bug,她不得不加班修复。公司给了她3个月的内部转岗期,如果转不出去就会被裁员。她得知公司可能会用Rust重写很多系统,于是就报名参加了公司的Rust培训,希望能够转型。
程序员吾真本
2024/08/29
5810
《Rust避坑式入门》第1章:挖数据竞争大坑的滥用可变性
【翻译】200行代码讲透RUST FUTURES (7)
我们将用一个伪reactor和一个简单的执行器创建我们自己的Futures,它允许你在浏览器中编辑和运行代码
MikeLoveRust
2020/08/05
1.3K0
Rust 中 Trait 的使用及实现分析
在 Rust 设计目标中,零成本抽象是非常重要的一条,它让 Rust 具备高级语言表达能力的同时,又不会带来性能损耗。零成本的基石是泛型与 trait,它们可以在编译期把高级语法编译成与高效的底层代码,从而实现运行时的高效。这篇文章就来介绍 trait,包括使用方式与三个常见问题的分析,在问题探究的过程中来阐述其实现原理。
MikeLoveRust
2021/05/11
2K0
【Rust 基础篇】Rust 智能指针
在 Rust 中,智能指针是一种提供了额外功能的指针类型。智能指针可以在编译时和运行时检查内存安全,并提供了更灵活的所有权和借用模型。本篇博客将详细介绍 Rust 中的智能指针,包括常用的智能指针类型、创建和使用智能指针、内存安全和性能考虑等。
繁依Fanyi
2023/10/12
2710
Rust那些事之并发Send与Sync
Send与Sync在Rust中属于marker trait,代码位于marker.rs,在标记模块中还有Copy、Unpin等trait。
公众号guangcity
2023/02/28
7340
Rust那些事之并发Send与Sync
rust类型转换
Rust 不提供原生类型之间的隐式类型转换(coercion),但可以使用 as 关键字进行显式类型转换(casting)。例如:
zy010101
2023/06/10
7330
Rust学习笔记Day18 智能指针Cow/MutexGuard
这是用于提供写时克隆(Clone-on-Write)的一个智能指针,和虚拟内存管理的写时复制很像。
用户1072003
2023/02/23
7190
Rust学习笔记Day18 智能指针Cow/MutexGuard
Rust学习笔记Day15 标记trait有哪些常用trait
昨天我们一起学习了 内存相关的3个常用trait Clone/Copy/Drop。
用户1072003
2023/02/23
3960
Rust学习笔记Day15 标记trait有哪些常用trait
2023学习日志
此时可以使用Box<T>指针指向嵌套的列表,得到cons list类型的结构体。(指针的内存大小是已知的,但列表的大小是在进行结构体声明时未知的)
TomoriNao
2023/07/27
1650
Rust漫画 #3 | 二次元 Rust Meetup 讨论会:Rewrite it in Rust 是否有害?
你好啊,作为一名程序员,参加线下的 Meetup 技术交流会也许是你唯一的社交活动。无论是线上还是线下,请都不要错过。今天,也许是你参加的第一次二次元 Rust Meetup 。
张汉东
2023/10/25
7570
Rust漫画 #3 |  二次元 Rust Meetup 讨论会:Rewrite it in Rust 是否有害?
聊聊Rust的并发约束:Send和Sync
不知道你有没有好奇过,Rust是怎么控制并发安全的。为什么编译器在编译时就能发现一些并发安全的问题。
newbmiao
2023/11/27
3210
聊聊Rust的并发约束:Send和Sync
Rust入坑指南:智能指针
在了解了Rust中的所有权、所有权借用、生命周期这些概念后,相信各位坑友对Rust已经有了比较深刻的认识了,今天又是一个连环坑,我们一起来把智能指针刨出来,一探究竟。
Jackeyzhe
2020/03/12
9050
聊聊共享所有权之Rc和Arc
像如下代码,字符串a如果直接移动给b后就没法后边再去打印,因为它的所有权已经转移给了b。
newbmiao
2023/11/27
3550
聊聊共享所有权之Rc和Arc
【译】Rust与智能指针
如果你一直在订阅这个系列,关于所有权的那篇文章[1]可能给你带来了这种印象——Rust 确实是个好东西,C++不应该在生产环境中使用。智能指针可能会改变你的想法。用现代的话来说,Smart pointers 是指那些有点(嗯......)额外(东西)的指针。他们本质上还是管理其所指向的对象的内存地址,并且当对象不再被使用的时候会将其释放。这消除了很多因不恰当的内存管理而引起的 bug,并使得编程不再那么枯燥乏味。C++智能指针为原始指针提供了一个安全的替代方案,而 Rust 智能指针则在保证安全的前提下扩展了语言功能。
MikeLoveRust
2020/10/26
1.1K0
【译】Rust与智能指针
rust写操作系统 rCore tutorial 学习笔记:实验指导五 驱动与文件
这是 os summer of code 2020 项目每日记录的一部分: 每日记录github地址(包含根据实验指导实现的每个阶段的代码):https://github.com/yunwei37/os-summer-of-code-daily
云微
2023/02/11
7830
Rust FFI 实践
部门算法团队开始成长起来,开始有越来越多的尝试以及成果,但是现在工程方面严重的限制了(主要是做预测服务)他们的研究成果转化为实际输出的能力。去年下半年,我们就发现TF官方的Java binding 存在严重的内存泄露问题,而TF Java binding 因为封装包括训练和预测所需要的API,比较复杂,我们也难以更改。同时,使用TF serving,就需要提供标准的RPC调用来完成交互,而所有的数据处理等工作都是在Java端,这也对运维模式产生一定的压力,毕竟要维护serving集群,研发工程师要对接serving才能完成一个端到端的预测。
用户2936994
2019/05/06
1.1K0
Rust特征对象(Trait Object)
前面学习的泛型,特征。它们都只能实现静态多态。它们和类型的绑定发生在编译期。如何让其实现C++中“父类指针指向子类对象”,从而实现运行时的多态。为了解决这个问题,Rust引入了——特征对象。
zy010101
2023/03/15
1K0
简述:Rust-1.38.0 RELEASE NOTE
mac OS更新,如果使用brew安装的,那么恭喜你,现在brew上面只能更新到1.37.0:
江湖安得便相忘
2019/10/08
3810
相关推荐
rust智能指针
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
本文部分代码块支持一键运行,欢迎体验
本文部分代码块支持一键运行,欢迎体验