改进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::*;

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

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

相关推荐
小码哥_常2 小时前
解锁AI编程密码:程序员常用的10个AI提示词
后端
九转成圣3 小时前
Java 性能优化实战:如何将海量扁平数据高效转化为类目字典树?
java·开发语言·json
SmartRadio3 小时前
ESP32-S3 双模式切换实现:兼顾手机_路由器连接与WiFi长距离通信
开发语言·网络·智能手机·esp32·长距离wifi
laowangpython3 小时前
Rust 入门:GitHub 热门内存安全编程语言
开发语言·其他·rust·github
我叫汪枫3 小时前
在后台管理系统中,如何递归和选择保留的思路来过滤菜单
开发语言·javascript·node.js·ecmascript
_.Switch3 小时前
东方财富股票数据JS逆向:secids字段和AES加密实战
开发语言·前端·javascript·网络·爬虫·python·ecmascript
软件技术NINI3 小时前
webkit简介及工作流程
开发语言·前端·javascript·udp·ecmascript·webkit·yarn
Brendan_0013 小时前
JavaScript的Stomp.over
开发语言·javascript·ecmascript
念2343 小时前
f5 shape分析
开发语言·javascript·ecmascript
苍穹之跃3 小时前
某量JS逆向
开发语言·javascript·ecmascript