💻博主现有专栏:
** C51单片机(STC89C516),c语言,c++,离散数学,算法设计与分析,数据结构,Python,Java基础,MySQL,linux,基于HTML5的网页设计及应用,Rust(官方文档重点总结),jQuery,前端vue.js,Javaweb开发,Python机器学习等
🥏主页链接:**** **Y小夜-CSDN博客
学习推荐:
在当今这个飞速发展的信息时代,人工智能(AI)已经成为了一个不可或缺的技术力量,它正在逐步改变着我们的生活、工作乃至整个社会的运作方式。从智能语音助手到自动驾驶汽车,从精准医疗到智慧城市,人工智能的应用已经渗透到了我们生活的方方面面。因此,学习和掌握人工智能相关的知识和技能,对于任何希望在这个时代保持竞争力的个人来说,都已经变得至关重要。 然而,人工智能是一个涉及数学、计算机科学、数据科学、机器学习、神经网络等多个领域的交叉学科,其学习曲线相对陡峭,对初学者来说可能会有一定的挑战性。幸运的是,随着互联网教育资源的丰富,现在有大量优秀的在线平台和网站提供了丰富的人工智能学习材料,包括视频教程、互动课程、实战项目等,这些资源无疑为学习者打开了一扇通往人工智能世界的大门。 前些天发现了一个巨牛的人工智能学习网站:前言 – 人工智能教程通俗易懂,风趣幽默,忍不住分享一下给大家。
实现Dereftrait 允许我们重载 解引用运算符(dereference operator)
*(不要与乘法运算符或通配符相混淆)。通过这种方式实现
Dereftrait 的智能指针可以被当作常规引用来对待,可以编写操作引用的代码并用于智能指针。
实现Dereftrait 允许我们重载 解引用运算符(dereference operator)
*(不要与乘法运算符或通配符相混淆)。通过这种方式实现
Dereftrait 的智能指针可以被当作常规引用来对待,可以编写操作引用的代码并用于智能指针。
🎯追踪指针的值
常规引用是一个指针类型,一种理解指针的方式是将其看成指向储存在其他某处值的箭头。在示例 15-6 中,创建了一个i32值的引用,接着使用解引用运算符来跟踪所引用的值:
fn main() { let x = 5; let y = &x; assert_eq!(5, x); assert_eq!(5, *y); }变量x存放了一个
i32值
5。
y等于
x的一个引用。可以断言
x等于
5。然而,如果希望对
y的值做出断言,必须使用
*y来追踪引用所指向的值(也就是 解引用),这样编译器就可以比较实际的值了。一旦解引用了
y,就可以访问
y所指向的整型值并可以与
5做比较。
🎯像引用一样使用Box<T>
fn main() { let x = 5; let y = Box::new(x); assert_eq!(5, x); assert_eq!(5, *y); }将y设置为一个指向
x值拷贝的
Box<T>实例,而不是指向
x值的引用。在最后的断言中,可以使用解引用运算符以
y为引用时相同的方式追踪
Box<T>的指针。接下来让我们通过实现自己的类型来探索
Box<T>能这么做有何特殊之处。
🎯自定义智能指针
为了体会默认情况下智能指针与引用的不同,让我们创建一个类似于标准库提供的Box<T>类型的智能指针。接着学习如何增加使用解引用运算符的功能。
从根本上说,Box<T>被定义为包含一个元素的元组结构体
struct MyBox<T>(T); impl<T> MyBox<T> { fn new(x: T) -> MyBox<T> { MyBox(x) } }这里定义了一个结构体MyBox并声明了一个泛型参数
T,因为我们希望其可以存放任何类型的值。
MyBox是一个包含
T类型元素的元组结构体。
MyBox::new函数获取一个
T类型的参数并返回一个存放传入值的
MyBox实例。
fn main() { let x = 5; let y = MyBox::new(x); assert_eq!(5, x); assert_eq!(5, *y); }得到的编译错误是:
$ cargo run Compiling deref-example v0.1.0 (file:///projects/deref-example) error[E0614]: type `MyBox<{integer}>` cannot be dereferenced --> src/main.rs:14:19 | 14 | assert_eq!(5, *y); | ^^ For more information about this error, try `rustc --explain E0614`. error: could not compile `deref-example` due to previous errorMyBox<T>类型不能解引用,因为我们尚未在该类型实现这个功能。为了启用
*运算符的解引用功能,需要实现
Dereftrait。
🎯通过实现Deref trait 将某一类型像引用一样处理
为了实现 trait,需要提供 trait 所需的方法实现。Dereftrait,由标准库提供,要求实现名为
deref的方法,其借用
self并返回一个内部数据的引用。示例 15-10 包含定义于
MyBox之上的
Deref实现:
use std::ops::Deref; impl<T> Deref for MyBox<T> { type Target = T; fn deref(&self) -> &Self::Target { &self.0 } }type Target = T;语法定义了用于此 trait 的关联类型。
没有Dereftrait 的话,编译器只会解引用
&引用类型。
deref方法向编译器提供了获取任何实现了
Dereftrait 的类型的值,并且调用这个类型的
deref方法来获取一个它知道如何解引用的
&引用的能力。
*(y.deref())Rust 将*运算符替换为先调用
deref方法再进行普通解引用的操作,如此我们便不用担心是否还需手动调用
deref方法了。Rust 的这个特性可以让我们写出行为一致的代码,无论是面对的是常规引用还是实现了
Deref的类型。
deref方法返回值的引用,以及
*(y.deref())括号外边的普通解引用仍为必须的原因在于所有权。如果
deref方法直接返回值而不是值的引用,其值(的所有权)将被移出
self。在这里以及大部分使用解引用运算符的情况下我们并不希望获取
MyBox<T>内部值的所有权。
注意,每次当我们在代码中使用*时,
*运算符都被替换成了先调用
deref方法再接着使用
*解引用的操作,且只会发生一次,不会对
*操作符无限递归替换,解引用出上面
i32类型的值就停止了,这个值与示例 15-9 中
assert_eq!的
5相匹配。
🎯函数和方法的隐式Deref强制转换
** Deref 强制转换*(deref coercions*)将实现了
Dereftrait 的类型的引用转换为另一种类型的引用。例如,Deref 强制转换可以将
&String转换为
&str,因为
String实现了
Dereftrait 因此可以返回
&str。Deref 强制转换是 Rust 在函数或方法传参上的一种便利操作,并且只能作用于实现了
Dereftrait 的类型。当这种特定类型的引用作为实参传递给和形参类型不同的函数或方法时将自动进行。这时会有一系列的
deref方法被调用,把我们提供的类型转换成了参数所需的类型。
Deref 强制转换的加入使得 Rust 程序员编写函数和方法调用时无需增加过多显式使用&和
*的引用和解引用。这个功能也使得我们可以编写更多同时作用于引用或智能指针的代码。
fn hello(name: &str) { println!("Hello, {name}!"); }当所涉及到的类型定义了Dereftrait,Rust 会分析这些类型并使用任意多次
Deref::deref调用以获得匹配参数的类型。这些解析都发生在编译时,所以利用 Deref 强制转换并没有运行时损耗!
🎯Deref强制转换如何与可变交互
类似于如何使用Dereftrait 重载不可变引用的
*运算符,Rust 提供了
DerefMuttrait 用于重载可变引用的
*运算符。
当
T: Deref<Target=U>时从&T到&U。当
T: DerefMut<Target=U>时从&mut T到&mut U。当
T: Deref<Target=U>时从&mut T到&U。头两个情况除了第二种实现了可变性之外是相同的:第一种情况表明如果有一个&T,而
T实现了返回
U类型的
Deref,则可以直接得到
&U。第二种情况表明对于可变引用也有着相同的行为。
第三个情况有些微妙:Rust 也会将可变引用强转为不可变引用。但是反之是 **不可能** 的:不可变引用永远也不能强转为可变引用。因为根据借用规则,如果有一个可变引用,其必须是这些数据的唯一引用(否则程序将无法编译)。将一个可变引用转换为不可变引用永远也不会打破借用规则。将不可变引用转换为可变引用则需要初始的不可变引用是数据唯一的不可变引用,而借用规则无法保证这一点。因此,Rust 无法假设将不可变引用转换为可变引用是可能的。
版权归原作者 Y小夜 所有, 如有侵权,请联系我们删除。