57-Deref 和 DerefMut
6 分钟阅读
译文 · 基于 Easy Rust
Deref 和 DerefMut
Deref是让你用*来解引用某些东西的trait。我们知道,一个引用和一个值是不一样的。
| |
而Rust连false都不给,因为它甚至不会比较两者。
| |
当然,这里的解法是使用*。所以这将打印出true。
| |
现在让我们想象一下一个简单的类型,它只是容纳一个数字。它就像一个Box,我们有一些想法为它提供一些额外的功能。但如果我们只是给它一个数字,
它就不能做那么多了。
我们不能像使用Box那样使用*:
| |
错误信息是:
| |
我们当然可以做到这一点。println!("{:?}", my_number.0 + 20);. 但是这样的话,我们就是在20的基础上再单独加一个u8。如果我们能把它们加在一起就更好了。cannot be dereferenced这个消息给了我们一个线索:我们需要实现Deref。实现Deref的简单东西有时被称为 “智能指针”。一个智能指针可以指向它的元素,有它的信息,并且可以使用它的方法。因为现在我们可以添加my_number.0,这是一个u8,但我们不能用HoldsANumber做其他的事情:到目前为止,它只有Debug。
有趣的是:String其实是&str的智能指针,Vec是数组(或其他类型)的智能指针。所以我们其实从一开始就在使用智能指针。
实现Deref并不难,标准库中的例子也很简单。下面是标准库中的示例代码。
| |
所以我们按照这个来,现在我们的Deref是这样的。
| |
所以现在我们可以用*来做:
| |
所以,这样就可以打印出40,我们不需要写my_number.0。这意味着我们得到了 u8 的方法,我们可以为 HoldsANumber 写出我们自己的方法。我们将添加自己的简单方法,并使用我们从u8中得到的另一个方法,称为.checked_sub()。.checked_sub()方法是一个安全的减法,它能返回一个Option。如果它能做减法,那么它就会在Some里面给你,如果它不能做减法,那么它就会给出一个None。记住,u8不能是负数,所以还是.checked_sub()比较安全,这样就不会崩溃了。
| |
这个打印:
| |
我们也可以实现DerefMut,这样我们就可以通过*来改变数值。它看起来几乎是一样的。在实现DerefMut之前,你需要先实现Deref。
| |
所以你可以看到,Deref给你的类型提供了强大的力量。
这也是为什么标准库说:Deref should only be implemented for smart pointers to avoid confusion。这是因为对于一个复杂的类型,你可以用 Deref 做一些奇怪的事情。让我们想象一个非常混乱的例子来理解它们的含义。我们将从一个游戏的 Character 结构开始。一个新的Character需要一些数据,比如智力和力量。所以这里是我们的第一个角色。
| |
现在让我们想象一下,我们要把人物的hit points放在一个大的vec里。也许我们会把怪物数据也放进去,把它放在一起。由于 hit_points 是一个 i8,我们实现了 Deref,所以我们可以对它进行各种计算。但是看看现在我们的main()函数中,它看起来多么奇怪。
| |
这只打印了[5, 5]。我们的代码现在让人读起来感觉非常奇怪。我们可以在main()上面看到Deref,然后弄清楚*billy的意思是i8,但是如果有很多代码呢?可能我们的代码有2000行,突然要弄清楚为什么要.push() *billy。Character当然不仅仅是i8的智能指针。
当然,写hit_points_vec.push(*billy)并不违法,但这让代码看起来非常奇怪。也许一个简单的.get_hp()方法会好得多,或者另一个存放角色的结构体。然后你可以迭代并推送每个角色的 hit_points。Deref提供了很多功能,但最好确保代码的逻辑性。