RUST 每日一省:解引用Deref

news/2024/10/21 5:49:27/

        “解引用”(Deref) 是“取引用”(Ref) 的反操作。 取引用, 我们有&、 &mut等操作符, 对应的, 解引用, 我们有*操作符。 默认的“取引用”、 “解引用”操作是互补抵消的关系, 互为逆运算。

fn main() {let v1 = 1;let p = &v1; //引用let v2 = *p; //解引用println!("{} {}", v1, v2);
} 

 上例中, v1的类型是i32, p的类型是&i32, *p的类型又返回i32。 

自定义解引用   

        自动解引用虽然是编译器来做的, 但是自动解引用的行为可以由开发者来定义。  
        通过实现 std::ops::Deref 和 std::ops::DerefMut  trait,可以修改解引用操作符 * 和 . 在自定义类型上的行为。
Deref的定义如下所示。 DerefMut的唯一区别是返回的是&mut型引用。

trait Deref {type Target: ?Sized;fn deref(&self) -> &Self::Target;
}
trait DerefMut: Deref {fn deref_mut(&mut self) -> &mut Self::Target;
} 

比如, 标准库中实现了Box的解引用: 

impl<T: ?Sized, A: Allocator> const Deref for Box<T, A> {type Target = T;
fn deref(&self) -> &T {&**self}
}

我们可以使用*y提取出来Box里的值。

fn main() {let x = 6;let y = Box::new(x);assert_eq!(6, x);assert_eq!(6, *y);//assert_eq!(6, y);  error    
}

        请大家注意这里的类型, deref() 方法返回的类型是&Target, 而不是Target。 如果说有变量y的类型为Box, *y的类型并不等于y.deref() 的类型。 *y的类型实际上是Target, 即i32。 


自动解引用 

        Rust提供的“自动解引用”机制, 是在某些场景下“隐式地”“自动地”帮我们做了一些事情。 什么是自动解引用呢? 

自动deref的规则是,

  • 当T: Deref<Target=U>时,允许&T转换为&U。 
  • 当T: DerefMut<Target=U>时,允许&mut T转换为&mut U。
  • 当T: Deref<Target=U>时,允许&mut T转换为&U。 
fn main() {let x = "hello world";let y = Box::new(String::from("hello world"));assert_eq!("hello world", x);assert_eq!("hello world", *y);
}

        Box实现了Deref trait,所以Rust可以通过调用deref来将Box<String>转换为&String。因为标准库为String提供的Deref实现会返回字符串切片,所以Rust可以继续调用deref来将&String转换为&str,并最终与“hello world”相匹配。 

 
冲突手动处理

        如果智能指针中的方法与它内部成员的方法冲突了怎么办呢? 编译器会优先调用当前最匹配的类型, 而不会执行自动deref, 在这种情况下, 我们就只能手动deref来表达我们的需求了。
clone方法在Rc和&str类型中都被实现了, 所以调用时会直接调用Rc的clone方法, 如果想调用Rc里面&str类型的clone方法, 则需要使用“解引用”操作符手动解引用。 

fn main() {let a = Rc::new(String::from("hello world"));let b = a.clone();let c = (*a).clone();   
}

在match表达式中 ,引用也需要手动解引用 

fn main() {let s = String::new();match &s {"" => {}_ => {}}
}


match后面的变量类型是&String, 匹配分支的变量类型为&'static str,这种情况下就需要我们手动完成类型转换了。 手动将&String类型转换为&str类型的办法如下。

1) match s.deref()。这个方法通过主动调用deref()方法达到类型转换的目的。 此时我们需要引入Deref trait方可通过编译, 即加上代码use std::ops::Deref; 。

2) match &*s。 我们可以通过*s运算符, 也可以强制调用deref()方法, 与上面的做法一样。

3) match s.as_ref() 。 这个方法调用的是标准库中的std::convert::AsRef方法, 这个trait存在于prelude中, 无须手工引入即可使用。

4) match s.borrow()。 这个方法调用的是标准库中的std::borrow::Borrow方法。 要使用它, 需要加上代码use std::borrow::Borrow; 

5) match &s[..]。 这个方案也是可以的, 这里利用了String重载的Index操作。


http://www.ppmy.cn/news/66111.html

相关文章

ChatGPT被封?了解这些原因避免账号被封!

随着企业数字化转型的加速&#xff0c;传统的商业模式正在被颠覆&#xff0c;越来越多的企业开始使用人工智能技术来推动业务发展。ChatGPT作为一种自然语言处理技术&#xff0c;可以通过对话方式回答用户的问题或提供信息&#xff0c;在企业市场营销、客户服务、销售等方面发挥…

【设计模式】责任链模式的介绍及其应用

责任链的介绍 责任链模式是一种对象的行为模式。在责任链模式里&#xff0c;很多对象由每一个对象对其下家的引用而连接起来形成一条链。请求在这个链上传递&#xff0c;直到链上的某一个对象决定处理此请求。发出这个请求的客户端并不知道链上的哪一个对象最终处理这个请求&a…

XML入门知识(一)——XML语法简介

目录 1 XML简介2 XML文档结构3 XML基本语法3.1 声明3.2 元素一 定义二 命名规则三 元素属性 3.3 实体引用3.4 注释 1 XML简介 XML 指可扩展标记语言&#xff08;eXtensible Markup Language&#xff09;。 XML的特点是&#xff1a; XML 指可扩展标记语言&#xff08;EXten…

绝地求生 压枪python版

仅做学习交流&#xff0c;非盈利&#xff0c;侵联删&#xff08;狗头保命) 一、概述 1.1 效果 总的来说&#xff0c;这种方式是通过图像识别来完成的&#xff0c;不侵入游戏&#xff0c;不读取内存&#xff0c;安全不被检测。 1.2 前置知识 游戏中有各种不同的枪械&#x…

计算机视觉——day 90 基于级联卷积神经网络和对抗学习的显著目标检测

基于级联卷积神经网络和对抗学习的显著目标检测 I. INTRODUCTIONII. 网路架构A. 基于级联卷积神经网络的生成器G全局显著性估计器 E局部显著性精炼器 R B.鉴别器 DIv. 实验A. 数据集和评价标准B. 实验结果 V. 结论 I. INTRODUCTION 显著目标检测在过去的几年中受到了广泛的关注…

互联网已退到了悬崖边上,只有借助新的概念,才能获得新发展

从某种程度上来看&#xff0c;我们与其将玩家们对于元宇宙、ChatGPT的追捧看成是他们对于这些新概念的前瞻性的认知&#xff0c;不如说他们在互联网的机制之下的确已经无法再找到新的发展红利和机会了。   因此&#xff0c;在人们对于元宇宙、ChatGPT等概念火热的大背景下&am…

深度思考:在 AI 时代,你会被放大一千倍的能力是什么?

Datawhale干货 作者&#xff1a;艾芙&#xff0c;复旦大学&#xff0c;百姓AI教育负责人 前言 大家晚上好&#xff0c;我是艾芙&#xff0c;百姓 AI 的 AI 教育负责人。 先做一下自我介绍&#xff0c;我是一个在技术圈和教育圈反复横跳的斜杠中年了。大约在 5 年前&#xff0c…

STM32F10X--EXTI--外部中断/事件控制器

一、EXTI是什么&#xff1f; EXTI&#xff08;External interrupt/event controller&#xff09;—外部中断/事件控制器&#xff0c;管理了控制器的20 个中断/事 件线。每个中断/事件线都对应有一个边沿检测器&#xff0c;可以实现输入信号的上升沿检测和下降沿的 检测。EXTI 可…