改进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 小时前
开发需掌握的知识:MQTT协议
java·后端
进击的荆棘2 小时前
C++起始之路——类和对象(下)
开发语言·c++
帅得不敢出门2 小时前
精简Android SDK(AOSP)的git项目提高git指令速度
android·java·开发语言·git·elasticsearch
liu****2 小时前
10.排序
c语言·开发语言·数据结构·c++·算法·排序算法
爱写Bug的小孙2 小时前
Tools、MCP 和 Function Calling
开发语言·人工智能·python·ai·ai编程·工具调用
小小Fred2 小时前
FreeRTOS函数prvInitialiseNewTask解析
java·开发语言
老华带你飞2 小时前
校务管理|基于springboot 校务管理系统(源码+数据库+文档)
java·数据库·vue.js·spring boot·后端·spring
JosieBook2 小时前
【部署】Spring Boot + Vue框架项目生产环境部署完整方案
vue.js·spring boot·后端
小杜的生信筆記2 小时前
基于R语言绘制网络图,新人选手上手
开发语言·r语言·生物信息学·组学
油丶酸萝卜别吃2 小时前
springboot项目中与接口文档有关的注解
java·spring boot·后端