Rust杂谈 如何禁止实现Drop trait

https://www.bilibili.com/video/BV1wMbTeXE5b/

大家好,今天给大家来分享一个新的话题,就是如何在Rust中禁止自定义实现Drop trait。

我们先看一下这里的代码。这边定义了一个struct User,然后我们给这个User实现了一个Drop trait。也就是当这个User被析构之前,就会打印一条这样的信息。我们来执行一下,可以看到没问题。这个main函数结束之前,User会被析构,然后drop函数会被调用,这行println会被执行。

在有的情况下,我们希望我们写的这个struct不让它实现Drop trait,就不让它自定义这个drop的函数逻辑。或者说现在没有定义,但是我不希望我代码的后面的维护者来定义这个Drop trait。这种需求是有的。

一种最容易想到的方案,其实就是我们提前把这个Drop trait给User实现好,并且将这个实现置为空,像这样。那么用户如果再次想给他添加Drop trait的话,他肯定会报冲突的。或者说我们可以把这个实现做得更为优雅一点,我这边通过一个宏定义,然后去匹配一个类型,然后我在这个宏的逻辑中为这个类型添加Drop trait,当然它的逻辑是空的。那么我们在这个User这里就可以直接声明一下这个宏。这个宏展开之后,struct User则自动带了一个空的Drop实现。那么如果我们再次尝试提供一个自定义的Drop trait实现的时候,这个代码肯定是没办法通过编译的,原因是我们不能对同一个类型两次实现Drop trait。

这种做法看似达成了我们的需求,但是它仍然有一个潜在的问题,那就是User仍然实现了Drop trait。我这边定义了一个叫做assert_drop的函数,然后它的参数是一个T,要求T是满足Drop的。很明显这个User是能够调用这个assert_drop函数的,原因是这个User有个隐藏的Drop trait实现。

这里给大家分享一种在不给结构体实现空Drop的情况下,就能达成我们需求的做法。这也是有些Rust的开源库的做法。

第一步,我们先实现一个自定义trait,我们这里取名叫MustNotImplDrop trait。它不需要任何函数定义,只需要空的就好。

接着我们给所有的实现了Drop trait的类型都实现下这个trait。这行代码应该大家都能看懂,也就是任意的类型T,只要这个T满足Drop,那么它就实现了这个trait。

接着我们改造一下这个宏,这里之前的实现是给这个类型添加一个Drop trait,我们这里把它改成这个MustNotImplDrop trait,然后它也不需要实现,就空了就行。

仅需这三步,我们的目的就已经达成了。我们可以看一下这个main函数,明显这里编译器已经开始报错了,原因是这个User并没有实现Drop trait。然后我们把这个自定义的Drop trait再打开看一下,可以看到编译器报错了,原因是这个User存在冲突的实现,也就是这个trait给这个User实现了两次。

这里我们简单来分析一下这个是怎么工作的。首先我们这里给User声明了这个宏,那么User一定是实现了这个trait的。当我们的开发者尝试给这个User添加Drop trait实现后,那么这个User就满足Drop trait,这种情况下,这条impl规则是能够匹配给User的。但是我们这里已经声明了一个宏,User已经有一个这个trait的实现。在Rust中,impl块是可以做特化的,但是有个要求,那就是不能有歧义,或者说能够匹配到多条规则。如果我们给User添加了Drop,那么这一条能够满足,同时这个宏又能导致这一条给满足,那么会触发编译错误,从而达到我们的效果。

这就是本节课的全部内容,感谢大家。

文章目录