改进rust代码的35种具体方法-类型(二十三)-避免通配符导入

上一篇文章-改进rust代码的35种具体方法-类型(二十二)-最小化可见度


Rust的use语句从另一个板条箱或模块中提取命名项目,并使该名称无需条件即可在本地模块的代码中使用。表单的通配符导入 (或glob导入use somecrate::module::*表示,该模块的每个公共符号都应添加到本地命名空间中。

作为次要版本升级的一部分,外部板条箱可能会将新项目添加到其API中;这被视为向后兼容的更改。

这两个观察结果的结合让人担心,对依赖项的不间断性更改可能会破坏您的代码:如果依赖项添加一个与您已经使用的名称相冲突的新符号,会发生什么?

在最简单的层面上,事实证明这不是问题:通配符导入中的名称被视为优先级较低,因此代码中的任何匹配名称都优先:

复制代码
use bytes::*;

// Local `Bytes` type does not clash with `bytes::Bytes`.
struct Bytes(Vec<u8>);

不幸的是,仍然有冲突可能发生的情况。例如,考虑依赖项添加新特征并为某种类型实现它的情况:

复制代码
trait BytesLeft {
    // Name clashes with the `remaining` method on the wildcard-imported
    // `bytes::Buf` trait.
    fn remaining(&self) -> usize;
}

impl BytesLeft for &[u8] {
    // Implementation clashes with `impl bytes::Buf for &[u8]`.
    fn remaining(&self) -> usize {
        self.len()
    }
}

如果新特征中的任何方法名称与适用于该类型的现有方法名称相冲突,那么编译器就无法再明确确定是哪种方法:

复制代码
let arr = [1u8, 2u8, 3u8];
let v = &arr[1..];

assert_eq!(v.remaining(), 2);

如编译时错误所示:

复制代码
error[E0034]: multiple applicable items in scope
  --> src/main.rs:40:18
   |
40 |     assert_eq!(v.remaining(), 2);
   |                  ^^^^^^^^^ multiple `remaining` found
   |
note: candidate #1 is defined in an impl of the trait `BytesLeft` for the
      type `&[u8]`
  --> src/main.rs:18:5
   |
18 |     fn remaining(&self) -> usize {
   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
   = note: candidate #2 is defined in an impl of the trait `bytes::Buf` for the
           type `&[u8]`
help: disambiguate the method for candidate #1
   |
40 |     assert_eq!(BytesLeft::remaining(&v), 2);
   |                ~~~~~~~~~~~~~~~~~~~~~~~~
help: disambiguate the method for candidate #2
   |
40 |     assert_eq!(bytes::Buf::remaining(&v), 2);
   |                ~~~~~~~~~~~~~~~~~~~~~~~~~

因此,您应该避免从您无法控制的 crate中导入通配符

如果您控制通配符导入源代码,那么先前提到的顾虑就会消失。例如,test模块通常会使用 use super::*;。对于主要使用模块来划分代码的 crate,也可能会从内部模块进行通配符导入。

复制代码
mod thing;
pub use thing::*;

然而,另一个常见的例外情况是,通配符导入是有意义的。一些 crate 采用惯例,将 crate 的常用项从一个预导模块重新导出,该模块明确地用于通配符导入:

复制代码
use thing::prelude::*;

虽然理论上同样的担忧也适用于这种情况,但在实践中,这样的前奏模块可能会被精心策划,更高的便利性可能超过未来问题的小风险。

最后,如果您没有遵循此项中的建议,请考虑将通配符导入的依赖项固定到精确版本,这样就不会自动允许对依赖项进行次要版本升级。

相关推荐
川川菜鸟1 分钟前
2025长三角数学建模C题完整思路
c语言·开发语言·数学建模
萌新下岸多多关照2 分钟前
Java中synchronized 关键字
java·开发语言
醍醐三叶5 分钟前
C++文件操作--2 二进制文件操作
开发语言·c++
剽悍一小兔10 分钟前
linux,我启动一个springboot项目, 用java -jar xxx.jar & ,但是没多久这个java进程就会自动关掉
开发语言
li星野14 分钟前
C++:C++内存管理
开发语言·c++
溟洵19 分钟前
【C++ Qt】布局管理器
开发语言·c++·qt
Source.Liu21 分钟前
【typenum】 8 常量文件(consts.rs)
rust
Stringzhua23 分钟前
JavaScript【5】DOM模型
开发语言·javascript·ecmascript
我家大宝最可爱27 分钟前
c++动态链接库
开发语言·c++
英英_42 分钟前
python 自动化教程
开发语言·python·自动化