Rust学习之Features

news/2025/3/15 22:40:12/

Rust学习之Features

    • 一 什么是 Features
    • 二 默认 feature
    • 三 简单的features应用示例
    • 四 可选(optional)的依赖
    • 五 依赖的特性
      • 5.1 在依赖表中指定
      • 5.2 在features表中指定
    • 六 命令行中特性控制
    • 七 特性统一路径
    • 八 其它
      • 8.1 相互排斥特性
      • 8.2 观察启用特性
      • 8.3 Feature resolver version 2

本文是学习Solana 程序库合约(SPL)的Rust 预先知识部分,需要有Rust基础

本文学习课程为https://doc.rust-lang.org/cargo/reference/features.html 。下面的内容为一些简单记录。

下面的内容中,feature和特性会交叉出现,但是均指同一概念。

一 什么是 Features

Features 是用来表达条件编译或者条件依赖的机制。

定义在Cargo.toml中的[features]表中的features 可以启用或者不启用。在构建时通过命令行参数--features来启用需要的特性,作为依赖启用特性时,直接在Cargo.toml中定义。

基本的features块定义为:

[features]
# Defines a feature named `webp` that does not enable any other features.
webp = []

在Rust中使用webp特性的代码示例:

// This conditionally includes a module which implements WEBP support.
#[cfg(feature = "webp")]
pub mod webp;// 下面是不启用"no-entrypoint"才包含entrypoint模块的定义
#[cfg(not(feature = "no-entrypoint"))]
mod entrypoint;

特性可以包含其它特性

[features]
bmp = []
png = []
ico = ["bmp", "png"]
webp = []// 注意,下面bmp和png的定义顺序任意,可以放在ico的下面

二 默认 feature

默认程序时不启用任何特性的,但是我们可以定义程序默认启用的features

[features]
default = ["ico", "webp"]
bmp = []
png = []
ico = ["bmp", "png"]
webp = []

三 简单的features应用示例

接着上面的Cargo.toml定义,一个简单的应用示例:

fn main() {#[cfg(feature = "webp")]println!("Hello webp!");#[cfg(not(feature = "webp"))]println!("Hello not webp!");#[cfg(feature = "ico")]println!("Hello ico!");#[cfg(feature = "gif")]println!("Hello gif!");
}

运行结果如下:

$ cargo run
Hello not webp!
Hello ico!$ cargo run --features webp
Hello webp!
Hello ico!

默认时仅启用了default未启用,所以上面cargo run输出了ico

注意:就算指定了--features参数,默认特性还是会启用。关闭它有两种方法:

  • 命令行参数使用--no-default-features
  • 作为依赖库,在定义时,设定default-features = false选项。

因此,我们如果在cargo run时不想启用默认特性,运行如下命令:

$ cargo run --features webp --no-default-features
Hello webp!

注意:原文提到了要小心默认特性设置,它通常启用了一些方便用户使用常用功能。但万一用户不想启用这些功能时,需要在所有依赖定义中限定default-features = false.特别当一个包被多处依赖时,每处定义都要指定default-features = false.

四 可选(optional)的依赖

依赖库也可以标记为optional,它意味着默认情况下不会被编译。例如如下定义:

[dependencies]
gif = { version = "0.11.1", optional = true }

默认时gif依赖是未启用的,那怎么启用它呢?其实上面的定义同时隐式定义了一个gif特性,类似如下定义:

[features]
gif = ["dep:gif"]

当然,我们无需手动写出上面的定义,但是如果你不想使用默认名称(和库名相同),就得手写一个了,如果你手写了,那么隐式特性就不存在了。例如如下示例:

[dependencies]
ravif = { version = "0.6.3", optional = true }
rgb = { version = "0.8.25", optional = true }[features]
avif = ["dep:ravif", "dep:rgb"]

上面的定义中,用户只能选择avif特性,阻止用户单独选择ravif或者rgb,因为也许这两者是必须同时启用的。

切记,采用上面的方式时,依赖必须是optional

五 依赖的特性

5.1 在依赖表中指定

在定义外部依赖的同时还可以同时指定启用的特性,例如:

[dependencies]
# Enables the `derive` feature of serde.
serde = { version = "1.0.118", features = ["derive"] }
flate2 = { version = "1.0.3", default-features = false, features = ["zlib"] }

注意:上面提到过,就算指定了依赖的features(不是feature,因为可以指定启用多个特性,所以为一数组,是带复数的s),其默认特性依然是开启的,因此我们必须手动关掉它,正如flate2定义。当然,如果别的地方同时也用到了flate2,那么无法保证其默认特性是关闭的,前面提到过原因。

5.2 在features表中指定

依赖的特性也可以在features表中定义(上面的是在dependencies表中定义),语法为package-name/feature-name,示例如下:

[dependencies]
jpeg-decoder = { version = "0.1.20", default-features = false }[features]
# Enables parallel processing support by enabling the "rayon" feature of jpeg-decoder.
parallel = ["jpeg-decoder/rayon"]

可以看到,这种方式定义时,关闭默认特性还是在dependencies表中进行。

注意当依赖为可选依赖时,package-name/feature-name语法还会同时启用该依赖,然而有时你却不想这么做,那么可以使用如下语法

package-name?/feature-name 。这样只有在其它别处启用该依赖后定义的特性才会被启用。示例如下:

[dependencies]
serde = { version = "1.0.133", optional = true }
rgb = { version = "0.8.25", optional = true }[features]
serde = ["dep:serde", "rgb?/serde"]

上面的定义中,启用serde特性会启用 serde 依赖库,但是只有在其它地方启用rgb依赖库了它才会启用rgbserde特性,例如我们定义了一个新的特性,其它即启用了rgb又包含了上面定义的serde特性。

(下面代码未验证,仅为个人猜想)

[features]
serde = ["dep:serde", "rgb?/serde"]
all= ["dep:rgb","serde"]

六 命令行中特性控制

  • --features FEATURES: 指定启用的特性,注意可以指定多个特性,它是一个列表,使用逗号或者空格分隔。如果使用空格分隔,注意在所有特性整体名称上加上双引号。例如我们接最初的toml及程序定义,运行示例:

    $ cargo run --features webp gif
    Hello webp!
    Hello ico!$ cargo run --features "webp gif"
    Hello webp!
    Hello ico!
    Hello gif!// 下面webp,gif中逗号前后可以有空格
    $ cargo run --features webp,gif --no-default-features 
    Hello webp!
    Hello gif!
    
  • --all-features: 启用所有特性

  • --no-default-features: 不启用默认特性

七 特性统一路径

当一个依赖在多个包中使用时,Cargo会使用所有启用特性的统一路径来标记它,从而确保只有一份单一的代码被使用(无重复代码)。

如下图所示:

在这里插入图片描述

此时,构建my-package时会启用winapi的四个特性。

特性中有一个共识是增加性,也就是启用一个特性不会关闭已有的功能,并且任意特性之间都可以联合使用。

例如 ,当你想支持no_std环境时,不要使用no_std特性(不要做减法),而是使用一个std特性来启用std库。示例代码如下:

#![no_std]#[cfg(feature = "std")]
extern crate std;#[cfg(feature = "std")]
pub fn function_that_requires_std() {// ...
}

上面的代码中,如果启用std特性,就使用std库,同时启用相应的函数。在revm (Rust EVM实现)中能见到大量这种用法,因为区块链运行环境并不一定支持std,所以统一使用#![no_std].

八 其它

8.1 相互排斥特性

通常不要这么设计(因为在确保任意特性联合都可以安全使用),但万一存在两个排斥特性时,需要进行检测并给出编译错误,例如

#[cfg(all(feature = "foo", feature = "bar"))]
compile_error!("feature \"foo\" and feature \"bar\" cannot be enabled at the same time");

三种替代方案为:

  • 分离不同功能到不同的包
  • 使用其中一个包替换另一个
  • 重构代码消除排斥性特性。

8.2 观察启用特性

cargo tree命令可以观察哪些特性被启用了,主要有这么几种用法

  • cargo tree -e features: 待研究
  • cargo tree -f "{p} {f}": 待研究
  • cargo tree -e features -i foo: 待研究

8.3 Feature resolver version 2

Feature resolver version 2

特研究


http://www.ppmy.cn/news/1340729.html

相关文章

(安卓)跳转应用市场APP详情页的方式

前言 最近在做一个需求,需要从自己APP进入到系统的应用市场 方便用户在应用市场给自己的APP打分 于是查阅了一些资料,下面说一下实现方法 实现方案 一般来说,最简单的方案就是这样: val uri Uri.parse("market://details…

归并排序+非比较排序

Hello everyone!欢迎来到排序章节目前的“终章”——归并排序,经过了前面三种排序的敲打,尤其是快速排序,相信你一定可以闯过这最后一关! 归并排序 基本思想: 归并排序(MERGE-SORT)…

net 一台路由器如何让两个不同网段的终端可以通信。

# 终端设备自己设置就行了 # 路由器的设置 The device is running! #################################################### <Huawei> Feb 1 2024 21:21:09-08:00 Huawei %%01IFPDT/4/IF_STATE(l)[0]:Interface GigabitEt hernet0/0/0 has turned into UP state. <…

2024牛客寒假算法基础集训营1

H&#xff1a;01背包&#xff0c;但是bit 这题一看数据范围很大&#xff0c;重量和价值都是1e8级别的&#xff0c;当时还在想是不是背包&#xff0c;原来就是位运算 具体来说&#xff0c;我们枚举m的每一位为1的1&#xff0c;强制这一位为0&#xff0c;这样m被分为前后两部分…

基于数据挖掘的微博事件分析与可视化大屏分析系统

设计原理&#xff0c;是指一个系统的设计由来&#xff0c;其将需求合理拆解成功能&#xff0c;抽象的描述系统的模块&#xff0c;以模块下的功能。功能模块化后&#xff0c;变成可组合、可拆解的单元&#xff0c;在设计时&#xff0c;会将所有信息分解存储在各个表中&#xff0…

Java学习笔记(十一)——常用类

一、包装类 &#xff08;一&#xff09;包装类和基本数据类型的转换 ​编辑 &#xff08;二&#xff09;包装类型和String类型的相互转换 &#xff08;三&#xff09;Integer类和Character类的常用方法 二、String &#xff08;一&#xff09;创建String对象的两种方式 …

GEDepth:Ground Embedding for Monocular Depth Estimation

参考代码&#xff1a;gedepth 出发点与动机 相机的外参告诉了相机在世界坐标系下的位置信息&#xff0c;那么可以用这个外参构建一个地面基础深度作为先验&#xff0c;后续只需要在这个地面基础深度先验基础上添加offset就可以得到结果深度&#xff0c;这样可以极大简化深度估…

2024 高级前端面试题之 Node 「精选篇」

该内容主要整理关于 Node 模块的相关面试题&#xff0c;其他内容面试题请移步至 「最新最全的前端面试题集锦」 查看。 Node模块精选篇 1. package.json版本号规则2. package.json 与 package-lock.json 的关3. npm 模块安装机制4. 模块化的差异 AMD CMD COMMONJS ESMODUL5. No…