7、Rust参考手册——属性

目录

  • [1. 属性总览](#1. 属性总览)
    • [1.1 元项属性语法](#1.1 元项属性语法)
    • [1.2 激活与懒惰属性](#1.2 激活与懒惰属性)
    • [1.3 工具属性](#1.3 工具属性)
    • [1.4 内建属性索引](#1.4 内建属性索引)
  • [2. 测试属性](#2. 测试属性)
    • [2.1 test属性](#2.1 test属性)
    • [2.2 ignore属性](#2.2 ignore属性)
    • [2.3 should_panic属性](#2.3 should_panic属性)
  • [3. 派生属性](#3. 派生属性)
    • [3.1 automatically_derived属性](#3.1 automatically_derived属性)
  • [4. 诊断属性](#4. 诊断属性)
    • [4.1 代码检查属性](#4.1 代码检查属性)
      • [4.1.1 代码检查原因](#4.1.1 代码检查原因)
      • 4.1.2 #expect属性
      • [4.1.3 代码检查组](#4.1.3 代码检查组)
      • [4.1.4 工具代码检查属性](#4.1.4 工具代码检查属性)
    • [4.2 deprecated属性](#4.2 deprecated属性)
    • [4.3 must_use属性](#4.3 must_use属性)
    • [4.4 diagnostic工具属性命名空间](#4.4 diagnostic工具属性命名空间)
      • [4.4.1 diagnostic::on_unimplemented属性](#4.4.1 diagnostic::on_unimplemented属性)
      • [4.4.2 diagnostic::do_not_recommend属性](#4.4.2 diagnostic::do_not_recommend属性)
  • [5. 代码生成属性](#5. 代码生成属性)
    • [5.1 inline属性](#5.1 inline属性)
    • [5.2 cold属性](#5.2 cold属性)
    • [5.3 naked属性](#5.3 naked属性)
    • [5.4 no_builtins属性](#5.4 no_builtins属性)
    • [5.5 target_feature属性](#5.5 target_feature属性)
      • [5.5.1 可用特性](#5.5.1 可用特性)
        • [5.5.1.1 x86或x86_64](#5.5.1.1 x86或x86_64)
        • [5.5.1.2 aarch64](#5.5.1.2 aarch64)
        • [5.5.1.3 loongarch](#5.5.1.3 loongarch)
        • [5.5.1.4 riscv32或riscv64](#5.5.1.4 riscv32或riscv64)
        • [5.5.1.5 wasm32或wasm64](#5.5.1.5 wasm32或wasm64)
        • [5.5.1.6 s390x](#5.5.1.6 s390x)
      • [5.5.2 附加信息](#5.5.2 附加信息)
    • [5.6 track_caller属性](#5.6 track_caller属性)
      • [5.6.1 行为](#5.6.1 行为)
      • [5.6.2 局限性](#5.6.2 局限性)
    • [5.7 instruction_set属性](#5.7 instruction_set属性)
      • [5.7.1 ARM上的instruction_set](#5.7.1 ARM上的instruction_set)
  • [6. 限制属性](#6. 限制属性)
    • [6.1 recursion_limit属性](#6.1 recursion_limit属性)
    • [6.2 type_length_limit属性](#6.2 type_length_limit属性)
  • [7. 类型系统属性](#7. 类型系统属性)
    • [7.1 non_exhaustive属性](#7.1 non_exhaustive属性)
  • [8. 调试器属性](#8. 调试器属性)
    • [8.1 debugger_visualizer属性](#8.1 debugger_visualizer属性)
      • [8.1.1 与Natvis一起使用](#8.1.1 与Natvis一起使用)
      • [8.1.2 与GDB一起使用](#8.1.2 与GDB一起使用)
    • [8.2 collapse_debuginfo属性](#8.2 collapse_debuginfo属性)
  • 参考

1. 属性总览

  AttrInput要么是DelimTokenTree,要么是以=开头,以Expression结尾。

  Attr要么是以SimplePath开头,以一个可选的AttrInput结尾,要么是以unsafe开头,,后面跟(加SimplePath加一个可选的AttrInput,以)结尾。

  OuterAttribute以#开头,后面跟加Attr,以结尾。

  InnerAttribute以#开头,后面跟!加加Attr,以结尾。

  属性是一种通用的、自由形式的元数据,其解释取决于名称、约定、语言和编译器版本。属性以ECMA-335中的Attributes为模型,语法则来自ECMA-334(C#)。

  内部属性以井号后的叹号熟悉额,应用于声明该属性所在的形式。

  示例:

rust 复制代码
// 应用于所在模块或crate的通用元数据
#![crate_type = "lib"]

// 内部属性应用于整个函数
fn some_unused_variables() {
	#![allow(unused_variables)]
	let x = ();
	let y = ();
	let z = ();
}

  外部属性以井号后不带叹号书写,应用于属性后面的形式。

  示例:

rust 复制代码
// 一个被标记为单元测试的函数
#[test]
fn test_foo() {

}

// 一个条件编译的模块
#[cfg(target_os = "linux")]
mod bar {

}

// 一个用于抑制警告/错误的代码检查属性
#[allow(non_camel_case_types)]
type int8_t = i8;

  属性由属性的路径组成,后接一个可选的定界token树,其解释由属性定义。宏属性之外的其他属性还允许输入为等号后接一个表达式。

  应用某些属性可能是不安全的。为避免在使用这些属性时产生未定义行为,必须满足编译器无法检查的某些义务。为断言这些义务已得到满足,属性被包裹在unsafe(...)中,例如#[unsafe(no_mangle)]。

  以下属性是不安全的:

  export_name

  link_section

  naked

  no_mangle

  属性可以分为以下几类:

  内建属性

  过程宏属性

  派生宏辅助属性

  工具属性

  属性可以应用于语言的许多形式:

  所有项声明都接受外部属性,而外部块、函数、实现和模块接受内部属性。

  大多数语句接受外部属性(表达式语句的限制见表达式属性)。

  块表达式接受外部和内部属性,但仅当它们是表达式语句的外部表达式或另一个块表达式的最终表达式(尾部表达式)时。

  枚举变体和结构体及联合体字段接受外部属性。

  匹配表达式分支接受外部属性。

  泛型生命周期或类型参数接受外部属性。

  表达式在有限的情况下接受外部属性。

  函数、闭包和函数指针参数接受外部属性。这包括函数指针和外部块中用···表示的可变参数上的属性。

  内联汇编模版字符串和操作数接受外部属性。只有某些属性在语义上被接受。

1.1 元项属性语法

  元项是大多数内建属性用于Attr规则的语法。

  MetaItem要么是SimplePath,要么是以SimplePath开头,后面跟=,以Expression结尾,要么是以SimplePath开头,后面跟(加一个可选的MetaSeq,以)结尾。

  MetaSeq以MetaItemInner开头,后面跟零个或多个,加MetaItemInner,以一个可选的,结尾。

  MetaItemInner要么是MetaItem,要么是Expression。

  元项中的表达式必须宏展开为字面量表达式,且不得包含整数或浮点类型后缀。不是字面量表达式的表达式在语法上被接受并且可以传给过程宏,但在解析之后会被拒绝。

  请注意,如果属性出现在另一个宏的内部,它会在哪个外部宏之后展开。例如,以下代码会先展开Serialize过程宏,而该过程宏必须保留include_str!调用,以便它随后被展开:

rust 复制代码
#[derive(Serialize)]
struct Foo {
	#[doc = include_str!("x.md")]
	x: u32,
}

  此外,属性中的宏只会在应用到该项的所有其他属性之后展开:

rust 复制代码
#[macro_attr1] // 首先展开
#[doc = mac!()] // mac!第四个展开
#[macro_attr2] // 第二个展开
#[derive(MacroDerive1, MacroDerive2)] // 第三个展开
fn foo() {}

  各种内建属性使用元项语法的不同子集来指定其输入。以下语法规则显示了一些常用的形式:

  MetaWord就是IDENTIFIER。

  MetaNameValueStr以IDENTIFIER开头,后面跟=,要么以STRING_LITERAL结尾,要么以RAW_STRING_LITERAL结尾。

  MetaListPaths以IDENTIFIER开头,后面跟(加一个可选的序列(以SimplePath开头,后面跟零个或多个,加SimplePath,以一个可选的,结尾),以)结尾。

  MetaListIdents以IDENTIFIER开头,后面跟(加一个可选的序列(以IDENTIFIER开头,后面跟零个或多个,加IDENTIFIER,以一个可选的,结尾),以)结尾。

  MetaListNameValueStr以IDENTIFIER开头,后面跟(加一个可选的序列(以MetaNameValueStr开头,后面跟零个或多个,加MetaNameValueStr,以一个可选的,结尾),以)结尾。

  元项的一些示例:

风格 示例
MetaWord no_std
MetaNameValueStr doc = "example"
MetaListPaths allow(unused, clippy::inline_always)
MetaListIdents macro_use(foo, bar)
MetaListNameValueStr link(name = "CoreFoundation", kind = "framework")

1.2 激活与懒惰属性

  属性要么是激活的,要么是惰性的。在属性处理期间,激活属性会从它们所在的形式上移出自身,而惰性属性会被保留下来。

  cfg和cfg_attr属性是激活的。属性宏是激活的。所有其他属性是惰性的。

1.3 工具属性

  编译器可以允许外部工具的属性,其中每个工具位于工具预导入中子集的模块内。属性路径的第一段是工具名称,后面一个或多个附加段的解释权归工具所有。

  当某个工具未被使用时,它的属性会被接受而不发出警告。当某个工具被使用时,处理和解释其属性的责任在于该工具。

  如果使用了no_implicit_prelude属性,则工具属性不可用。

rust 复制代码
// 告诉rustfmt工具不要格式化下面的元素
#[rustfmt::skip]
struct S {
}

// 控制clippy工具的"圈复杂度"阈值
#[clippy::cyclomatic_complexity = "100"]
pub fn f() {}

  rustc目前识别的工具有clippy、rustfmt、diagnostic、miri和rust_analyzer。

1.4 内建属性索引

  以下是所有内建属性的索引。

  条件编译:

  cfg------控制条件编译

  cfg_attr------有条件地包含属性

  测试:

  test------将函数标记为测试

  ignore------禁用测试函数

  should_panic------表示测试应产生panic

  派生:

  derive------自动trait实现

  automatically_derived------由derive创建的实现的标记

  宏:

  macro_export------导出macro_rules宏以供跨crate使用

  macro_use------展开宏可见性,或从其他crate导入宏

  proc_macro------定义类函数宏

  proc_macro_derive------定义派生宏

  proc_macro_attribute------定义属性宏

  诊断:

  allow、expect、warn、deny、forbid------更改默认的代码检查级别

  deprecated------生成弃用通知

  must_use------为未使用的值生成代码检查

  diagnostic::on_unimplemented------提示编译器在trait未被实现时发出特定的错误消息

  diagnostic::do_not_recommend------提示编译器在错误消息中不显示某个trait实现。

  ABI、链接、符号和FFI:

  link------指定与外部块链接的原生库

  link_name------指定与外部块中函数或静态项的符号名称

  link_ordinal------指定外部块中函数或静态项符号的序号

  no_link------阻止链接外部crate

  repr------控制类型布局

  crate_type------指定crate的类型

  no_main------禁止发出main符号

  export_name------为函数或静态项指定导出符号名称

  link_section------指定函数或静态项使用的对象文件节

  no_mangle------禁用符号名称编码

  used------强制编译器在输出对象文件中保留静态项

  crate_name------指定crate名称

  代码生成:

  inline------内联代码的提示

  cold------提示函数不太可能被调用

  naked------阻止编译器发出函数的序言和尾声

  no_builtins------禁用某些内建函数的使用

  target_feature------配置平台特定的代码生成

  track_caller------将父调用位置传给std::panic::Location::caller()

  instruction_set------指定用于生成函数代码的指令集

  文档:

  doc------指定文档。文档注释会转换为doc属性

  预导入:

  no_std------从预导入中移除std

  no_implicit_prelude------禁用模块内的预导入查找

  模块:

  path------指定模块的文件名

  限制:

  recursion_limit------设置某些编译器操作的最大递归限制

  type_length_limit------设置多态类型的最大大小

  运行时:

  panic_handler------设置处理panic的函数

  global_allocator------设置全局内存分配处理器

  windows_subsystem------指定链接的Windows子系统

  特性:

  feature------用于启用不稳定的或实验性的编译器特性。

  类型系统:

  non_exhaustive------表示类型将来会增加更多字段/变体

  调试器:

  debugger_visualizer------嵌入一个指定类型调试器输出的文件

  collapse_debuginfo------控制宏调用在调试信息中的编码方式

2. 测试属性

  以下属性用于指定执行测试的函数。以测试模式编译crate时,会连同测试函数一起构建一个测试框架,用于执行这些测试。启用测试模式还会启用test条件编译选项。

2.1 test属性

  test属性将函数标记为作为测试执行。

  示例:

rust 复制代码
pub fn add(left: u64, right: u64) -> u64 { left + right }

#[test]
fn it_works() {
	let result = add(2, 2);
	assert_eq!(result, 4);
}

  test使用MetaWord语法。test属性只能应用于自由函数,要求函数是单态的、不接受任何参数,且返回类型实现了Termination trait。

  实现Termination trait的一些类型包括:()、Result<T, E> where T: Termination, E: Debug。

  一个函数上只有第一次使用test有效。rustc会对第一次之后的使用发出代码检查警告。这在未来可能成为错误。

  test属性从标准库预导入导出,即std::prelude::v1::test。

  这些函数只在测试模式下才编译。

  测试模式通过向rustc传入--test参数,或使用cargo test来启动。

  测试框架会调用返回值的report方法,并根据产生的ExitCode是否表示成功终止来判定测试通过或失败。具体来说:

  返回()的测试只要正常终止且不panic即通过。

  返回Result<(), E>的测试只要返回Ok(())即通过。

  返回ExitCode::SUCCESS的测试通过,返回ExitCode::FAILURE的测试失败。

  未终止的测试既不通过也不失败。

  示例:

rust 复制代码
use std::io;

fn setup_the_thing() -> io::Result<i32> { Ok(1) }
fn do_the_thing(s: &i32) -> io::Result<()> { Ok(()) }

#[test]
fn test_the_thing() -> io::Result<()> {
	let state = setup_the_thing()?;
	do_the_thing(&state)?;
	Ok(())
}

2.2 ignore属性

  ignore属性可以与test属性一起使用,告诉测试框架不要将该函数作为测试执行。

rust 复制代码
#[test]
#[ignore]
fn check_thing() {

}

  rustc的测试框架支持--include-ignored标志,以强制运行被忽略的测试。

  ignore属性使用MetaWord和MetaNameValueStr两种语法。ignore的MetaNameValueStr形式提供了一种指定测试被忽略原因的方式。

  示例:

rust 复制代码
#[test]
#[ignore = "not yet implemented"]
fn mytest() {

}

  ignore属性只能应用于带有test属性的函数。rustc会忽略在其他位置的使用,但会对其发出代码检查警告。这在未来可能成为错误。

  一个函数上只有第一次使用ignore有效。rustc会对第一次之后的使用发出代码检查警告。这在未来可能成为错误。

  被忽略的测试在测试模式下仍然会被编译,但不会被执行。

2.3 should_panic属性

  should_panic属性使测试仅在该属性所应用的测试函数发生panic时才通过。

  示例:

rust 复制代码
#[test]
#[should_panic(expected = "values don't match")]
fn mytest() {
	assert_eq!(1, 2, "values don't match");
}

  should_panic属性有以下几种形式:

  MetaWord

  示例:

rust 复制代码
#[test]
#[should_panic]
fn mytest() {
	panic!("error: some message, and more");
}

  MetaNameValueStr------给定的字符串必须出现在panic消息中,测试才能通过。

  示例:

rust 复制代码
#[test]
#[should_panic = "some message"]
fn mytest() {
	panic!("error: some message, and more");
}

  MetaListNameValueStr------与MetaNameValueStr语法一样,给定的字符串必须出现在panic消息中。

  示例:

rust 复制代码
#[test]
#[should_panic(expected = "some message")]
fn mytest() {
	panic!("error: some message, and more");
}

  should_panic属性只能应用于带有test属性的函数。rustc会忽略在其他位置的使用,但会对其发出代码检查警告。这在未来可能成为错误。

  一个函数上只有第一次使用should_panic有效。rustc会对第一次之后的使用发出带未来兼容性警告的代码检查。这在未来可能成为错误。

  当使用MetaNameValueStr形式或使用带有expected键的MetaListNameValueStr形式时,给定的字符串必须出现在panic消息中的某处,测试才能通过。

  测试函数的返回类型必须是()。

3. 派生属性

  派生属性调用一个或多个派生宏,允许为数据结构自动生成新的项。你可以通过过程宏创建派生宏。

  示例:PartialEq派生宏为Foo<T> where T: PartialEq生成一个PartialEq的实现。Clone派生宏同样为Clone生成实现。

rust 复制代码
#[derive(PartialEq, Clone)]
struct Foo<T> {
	a: i32,
	b: T,
}

  生成的实现等价于:

rust 复制代码
struct Foo<T> {
	a: i32,
	b: T,
}

impl<T: PartialEq> PartialEq for Foo<T> {
	fn eq(&self, other: &Foo<T>) -> bool {
		self.a == other.a && self.b == other.b
	}
}

impl<T: Clone> Clone for Foo<T> {
	fn clone(&self) -> Self {
		Foo { a: self.a.clone(), b: self.b.clone() }
	}
}

  派生属性使用MetaListPaths语法,指定要调用的派生宏的路径列表。派生属性只能应用于结构体、枚举和联合体。

  派生属性可以在一个项上任意多次使用。所有属性中列出的所有派生宏都会被调用。

  派生属性在标准库中以下列形式导出:

  core::derive

  std::derive

  core::prelude::v1::derive

  std::prelude::v1::derive

  内建派生定义在语言预导入中。内建派生的列表如下:

  Clone

  Copy

  Debug

  Default

  Eq

  Hash

  Ord

  PartialEq

  PartialOrd

  内建派生会在它们生成的实现上包含automatically_derived属性。

  在宏展开期间,对于派生列表中的每个元素,相应的派生宏会展开为零个或多个项。

3.1 automatically_derived属性

  automatically_derived属性用于标注一个实现,表示它是派生宏自动创建的。它没有直接效果,但工具和诊断代码检查可以用它来检测这些自动生成的实现。

  示例:给定struct Example上的#derive(Clone),该派生宏可能会生成:

rust 复制代码
struct Example;

#[automatically_derived]
impl ::core::clone::Clone for Example {
	#[inline]
	fn clone(&self) -> Self {
		Example
	}
}

  automatically_derived属性使用MetaWord语法。automatically_derived属性只能应用于实现。rustc会忽略在其他位置的使用,但会对其发出代码检查警告。这在未来可能成为错误。

  在一个实现上多次使用automatically_derived与使用一次效果相同。rustc会对第一次之后的使用发出代码检查警告。

  automatically_derived属性没有任何行为。

4. 诊断属性

  以下属性用于在编译期间控制或生成诊断消息。

4.1 代码检查属性

  代码检查命名一种可能不可取的编码模式,例如不可达代码或缺漏的文档。

  allow、expect、warn、deny和forbid这些代码检查属性使用MetaListPaths语法指定一组代码检查名称,以更改属性所应用实体的代码检查级别。

  对于任意代码检查C:

  #[allow(C)]覆盖对C的检查,使违规行为不被报告。

  #[expect(C)]表示预期会发出代码检查C。该属性将抑制C的发出;若预期未实现,则会发出一个警告。

  #[warn(C)]对C的违规发出警告,但继续编译。

  #[deny(C)]在遇到C的违规后发出错误信息。

  #[forbid(C)]与#[deny(C)]相同,但还禁止之后更改该代码检查级别。

  rustc支持的代码检查可以通过rustc -W help查看连同它们的默认设置,并在rustc手册中有文档说明。

rust 复制代码
pub mod m1 {
	// 缺少文档在这里被忽略
	#[allow(missing_docs)]
	pub fn undocumented_one() -> i32 { 1 }
	// 缺少文档在这里产生警告
	#[warn(missing_docs)]
	pub fn undocumented_too() -> i32 { 2 }
	// 缺少文档在这里产生错误
	#[deny(missing_docs)]
	pub fn undocumented_end() -> i32 { 3 }
}

fn main() {}

  代码检查属性可以覆盖先前属性指定的级别,只要该级别不试图更改已被forbid的代码检查(deny除外:它在forbid上下文中被允许,但会被忽略)。先前属性指语法树中更高层级的属性,或同一个实体上按从左到右源码顺序排列的先前属性。

  以下示例展示如何使用allow和warn来开关某个检查:

rust 复制代码
#[warn(missing_docs)]
pub mod m2 {
	#[allow(missing_docs)]
	pub mod nested {
		// 缺少文档在这里被忽略
		pub fn undocumented_one() -> i32 { 1 }

		// 缺少文档在这里产生警告
		// 尽管上面有allow
		#[warn(missing_docs)]
		pub fn undocumented_two() -> i32 { 2 }
	}

	// 缺少文档在这里产生警告
	pub fn undocumented_too() -> i32 { 3 }
}

fn main() {}

  以下示例展示如何使用forbid来禁止对该代码检查使用allow或expect:

rust 复制代码
#[forbid(missing_docs)]
pub mod m3 {
	// 在此尝试切换警告会产生错误。
	#[allow(missing_docs)]
	/// 返回2
	pub fn undocumented_too() -> i32 { 2 }
}

fn main() {}

  rustc允许在命令行上设置代码检查级别,并且支持对报告的代码检查设置上限。

4.1.1 代码检查原因

  所有代码检查属性都支持额外的reason参数,用于说明添加某个属性的原因。如果代码检查以定义的级别发出,该原因将作为代码检查消息的一部分展示。

rust 复制代码
// 'keyword_idents'默认是允许的,这里我们将其deny
// 以避免在更改版本时迁移标识符
#![deny(
	keyword_idents,
	reason = "we want to avoid these idents to future compatible"
)]

// 这个名称在Rust 2015中是允许的。我们仍然力求避免使用它,
// 以保持未来兼容,不迷惑最终用户
fn dyn() {}

  下面是另一个示例,代码检查带有reason被允许:

rust 复制代码
use std::path::PathBuf;

pub fn get_path() -> PathBuf {
	// allow属性上的reason参数对读者起到文档的作用
	#[allow(unused_mut, reason = "this is only modified on some platforms")]
	let mut file_name = PathBuf::from("git");
	#[cfg(target_os = "windows")]
	file_name.set_extension("exe");
	file_name
}

4.1.2 #expect属性

  #[expect(C)]属性为代码检查C创建一个代码检查预期。如果在同一位置放置#[warn(C)]属性会产生代码检查发出,则该预期被视为实现。如果预期未被实现------因为代码检查C不会被发出------就会在该属性处发出unfulfilled_lint_expectations代码检查。

rust 复制代码
fn  main() {
	// 这个#[expect]属性创建了一个代码检查预期:
	// unused_variables代码检查会被下面的语法发出,但这个预期没有实现那
	// 因为question变量被println!宏使用了
	// 因此,会在该属性处发出unfulfilled_lint_expectations代码检查
	#[expect(unused_variables)]
	let question = "who lives in a pineapple under the sea?";
	println!("{question}");
	
	// 这个#[expect]属性创建的代码检查预期会被实现
	// 因为answer变量从未被使用,通常会被发出的
	// unused_variables代码检查被抑制了。该语句或属性不会产生任何警告
	#[expect(unused_variables)]
	let answer = "SpongeBob SquarePants!";
}

  代码检查预期只会被由expect属性抑制的代码检查发出所实现。如果作用域内allow或warn等其他级别属性修改了代码检查级别,代码检查的发出将按相应方式处理,而预期将保持未实现状态。

rust 复制代码
#[expect(unused_variables)]
fn select_song() {
	// 这会以warn级别发出unused_variables代码检查
	// 正如warn属性所定义的那样
	// 这不会实现函数上方的预期
	#[warn(unused_variables)]
	let song_name = "Crab Rave";
	// allow属性抑制了代码检查的发出
	// 这不会实现预期,因为它被allow属性抑制了
	// 而不是函数上方的expect属性
	#[allow(unused_variables)]
	let song_creator = "Noisestorm";
	// 这个expect属性会在该变量处抑制unused_variables代码检查的发出
	// 函数上方的expect属性仍然不会实现
	// 因为这次代码检查的发出被局部的expect属性抑制了
	#[expect(unused_variables)]
	let song_version = "Monstercat Release";
}

fn main() {}

  如果expect属性包含多个代码检查,则每个代码检查都分别被预期。对于代码检查组,组内只要有一个代码检查被发出就足够了。

rust 复制代码
// 这个预期会被函数内的未使用值实现
// 因为发出的unused_variables代码检查属于unused代码检查组
#[expect(unused)]
pub fn thoughts() {
	let unused = "I'm running out of examples";
}

pub fn another_example() {
	// 这个属性创建了两个代码检查预期。unused_mut代码检查将被抑制
	// 从而实现第一个预期。unused_variables不会被发出,因为该变量被使用了
	// 因此那个预期将不会被满足,并会发出一个警告
	#[expect(unused_mut, unused_variables)]
	let mut link = "https://www.rust-lang.org/";
	println!("Welcome to our community: {link}");
}

fn main() {}

  #[expect(unfulfilled_lint_expectations)]的行为当前被定义为总是生成unfulfilled_lint_expectations代码检查。

4.1.3 代码检查组

  代码检查可以组织成具名组,以便相关代码检查的级别可以一起调整。使用具名组等价于列出该组内的所有代码检查。

rust 复制代码
// 这允许unused组中的所有代码检查
#[allow(unused)]
// 这将unused组中的unused_must_use代码检查覆盖为deny
#[deny(unused_must_use)]
fn example() {
	// 这个不会产生警告,因为unused_variables代码检查属于unused组
	let x = 1;
	// 这会产生错误,因为结果未被使用
	// 且unused_must_use被标记为deny
	std::fs::remove_file("some_file"); // 错误:未使用的Result必须被使用
}

fn main() {}

  有一个特殊的组名为warnings,它包含所有位于warn级别的代码检查。warnings组忽略属性顺序,并作用于实体内所有本会发出警告的代码检查。

rust 复制代码
unsafe fn an_unsafe_fn() {}
// 这两个属性的顺序无关紧要
#[deny(warnings)]
// unsafe_code代码检查默认通常是allow
#[warn(unsafe_code)]
fn example_err() {
	// 这是一个错误,因为unsafe_code警告已被提升为deny
	unsafe { an_unsafe_fn() } // 错误:使用了unsafe块
}

fn main() {}

4.1.4 工具代码检查属性

  工具代码检查允许使用带作用域的代码检查,对特定工具的代码检查进行allow、warn、deny或forbid。

  工具代码检查只在关联工具处于激活状态时才会被检查。如果allow等代码检查属性引用了一个不存在的工具代码检查,编译器在你使用该工具之前不会对不存在的代码检查发出警告。

  除此之外,它们的工作方式与普通代码检查属性完全相同。

rust 复制代码
// 将整个pedantic clippy代码检查组设置为warn
#![warn(clippy::pedantic)]
// 抑制来自filter_map clippy代码检查的警告
#![allow(clippy::filter_map)]

fn main() {}

// 仅针对这个函数抑制cmp_nan clippy代码检查
#[allow(clippy::cmp_nan)]
fn foo() {}

  rustc目前识别的工具代码检查有clippy和rustdoc。

4.2 deprecated属性

  deprecated属性将项标记为已弃用。rustc会对#[deprecated]项的使用发出警告。rustdoc会显示项的弃用信息,包括since版本和note(如果有)。

  deprecated属性有几种形式:

  deprecated------发出通用消息

  deprecated = "message"------在弃用消息中包含给定的字符串

  MetaListNameValueStr语法,带两个可选字段:

  since------指定该项被弃用时的版本号。rustc不会解释该字符串,但Clippy等外部工具可能会检查其值的有效性。

  note------指定应包含在弃用消息中的字符串。通常用于提供关于弃用原因和推荐替代方案的说明。

  deprecated属性可以应用于任何项、trait项、枚举变体、结构体字段、外部块项或宏定义。它不能应用于trait实现项。当应用于包含其他项的项(如模块或实现)时,所有子项都会继承该弃用属性。

  示例:

rust 复制代码
#[deprecated(since = "5.2.0", note = "foo was rarely used. Users should instead use bar")]
pub fn foo() {}

pub fn bar() {}

fn main() {
	foo();
}

4.3 must_use属性

  must_use属性标记一个应当被使用的值。

  must_use属性使用MetaWord和MetaNameValueStr语法。

  示例:

rust 复制代码
#[must_use]
fn use_me1() -> u8 { 0 }

#[must_use = "explanation of why it should be used"]
fn use_me2() -> u8 { 0 }

  must_use属性可以应用于:

  结构体

  枚举

  联合体

  函数

  trait

  rustc会忽略在其他位置的使用,但会对其发出代码检查警告。这在未来可能成为错误。

  must_use属性在一个项只能使用一次。rustc会对第一次之后的使用发出代码检查警告。这在未来可能成为错误。

  must_use属性可以通过MetaNameValueStr语法包含一条消息,例如#[must_use = "example message"]。这消息可以作为代码检查的一部分被发出。

  当该属性应用于结构体、枚举或联合体时,如果表达式语句的表达式具有该类型,则使用它会触发unused_must_use代码检查。

rust 复制代码
#![deny(unused_must_use)]

#[must_use]
struct MustUse();

fn main() {
	MustUse(); // 错误:未使用的值,必须使用
}

  作为类型规则的一个例外,当E无人时,该代码检查不会为Result<(), E>触发;当B无人时,也不会为ControlFlow<B, ()>触发。来自外部crate的#[non_exhaustive]类型不会为此目的被视为无人类型,因为它将来可能会增加构造器。

rust 复制代码
#![deny(unused_must_use)]

use core::ops::ControlFlow;

enum Empty {}

fn f1() -> Result<(), Empty> { Ok(()) }
fn f2() -> ControlFlow<Empty, ()> { ControlFlow::Continue(()) }

fn main() {
	f1();
	f2();
}

  如果表达式语句的表达式是调用表达式或方法调用表达式,且其函数操作数是应用了该属性的函数,则使用它会触发unused_must_use代码检查。

rust 复制代码
#[must_use]
fn f() {}

fn main() {
	f(); // 错误:未使用的返回值,必须使用
}

  如果表达式语句的表达式是调用表达式或方法调用表达式,且其函数操作数是一个返回impl trait或dyn trait类型的函数,而约束中的一个或多个trait标记了该属性,则使用它会触发unused_must_use代码检查。

rust 复制代码
#[must_use]
trait Tr {}

impl Tr for () {}

fn f() -> impl Tr {}

fn main() {
	f(); // 错误:未使用的实现者,必须使用
}

  当属性应用于trait声明中的函数时,如果调用表达式或方法调用表达式的函数操作数是该函数的实现,则使用它会触发unused_must_use代码检查。

rust 复制代码
trait Tr {
	#[must_use]
	fn use_me(&self);
}

impl Tr for () {
	fn use_me(&self) {}
}

fn main() {
	().use_me(); // 错误:未使用的返回值,必须使用
	<() as Tr>::use_me(&());
}

  当检查表达式语句的表达式是否符合上面规则时,该代码检查会透过块表达式(包括unsafe块和带标签的块表达式)查看每个块表达式的尾部表达式。对于嵌套块表达式,这一规则递归适用。

rust 复制代码
#[must_use]
fn f() {}

fn main() {
	{
		f(); // 错误:该代码检查会透过块表达式查看
	}
	unsafe { f(); } // 错误:该代码检查会透过unsafe块查看
	{ { f(); } } // 错误:该代码检查会透过嵌套块查看
}

  当用于trait实现中的函数时,该属性不执行任何操作。

rust 复制代码
trait Tr {
	fn f(&self);
}

impl Tr for () {
	#[must_use] // 这没有效果
	fn f(&self) {}
}

fn main() {
	().f();
}

  rustc会对trait实现中函数上的使用发出代码检查警告。这在未来可能成为错误。

  将#[must_use]函数的返回结果包裹在特定表达式中,可以抑制基于fn的检查,因为该表达式语句的表达式不是对#[must_use]函数的调用表达式或方法调用表达式。如果整个表达式的类型是#[must_use],基于类型的检查仍然适用。

rust 复制代码
#![deny(unused_must_use)]
#[must_use]
fn f() {}

// 对于下面这些,基于fn的检查都不会触发
// 因为表达式语句的表达式不是对#[must_use]函数的调用
fn main() {
	(f(), ); // 表达式是元组,不是调用
	Some(f()); // 被调用者Some不是#[must_use]
	if true {
		f();
	} else {
	} // 表达式是if,不是调用
	match true { // 表达式是match,不是调用
		_ => f();
	};
}
rust 复制代码
#![deny(unused_must_use)]
#[must_use]
struct MustUse;
fn g() -> MustUse { MustUse }

// 尽管if表达式不是调用,基于类型的检查仍会触发
// 因为表达式的类型是带有#[must_use]属性的MustUse
fn main() {
	if true {
		g()
	} else {
		MustUse
	}; // 错误:必须被使用
}

  当有意识丢弃一个必须使用的值时,使用_作为模式的let语句或解构赋值是惯用法。

rust 复制代码
#![deny(unused_must_use)]
#[must_use]
fn f() {}

fn main() {
	let _ = f();
	_ = f();
}

4.4 diagnostic工具属性命名空间

  #[diagnostic]属性命名空间时承载用于影响编译期错误消息的各种属性的场所。这些属性所提供的提示并不保证被采用。

  此命名空间的位置属性会被接受,尽管它们可能会针对未使用的属性发出警告。此外,对已知属性传入无效输入通常只会产生警告。这样设计的目的是,允许在未来添加或废弃属性、更改输入,而无需让那些无实际意义的属性或选项继续工作。

4.4.1 diagnostic::on_unimplemented属性

  #[diagnostic::on_unimplemented]属性是对编译器的一种提示,用于补充在类型上需要某个trait但未实现的场景下通常会生成的错误消息。

  该属性应该放置在trait声明上,不过放在其他位置也不算错误。

  该属性使用MetaListNameValueStr语法来指定其输入,不过对该属性的任何格式错误的输入都不被视为错误,以同时保证向前与向后兼容。

  以下键具有给定含义:

  message------顶层错误消息的文本

  label------在错误消息中,错误代码内部联显示的标签文本

  note------提供附加说明

  note选项可以出现多次,这会发出多条说明信息。

  如果其他选项中的任何一个出现多次,则相关选项中首次出现的那一个指定实际使用的值。后续出现会生成警告。对任何未知的键都会生成警告。

  上述三个选项都接受一个字符串作为参数,其解释方式与std::fmt字符串相同的格式。

  具有给定命名参数的格式参数会被替换为以下文本:

  {Self}------实现该trait的类型的名称

  {GenericParameterName}------给定泛型参数的泛型实参的类型名称

  任何其他格式参数都会生成警告,但会按原样保留在字符串中。无效的格式字符串可能会生成警告,但除此之外是允许的,只不过可能无法按预期方式显示。格式说明符可能会生成警告,但除此之外被忽略。

  示例:

rust 复制代码
#[diagnostic::on_unimplemented(
	message = "My Message for `ImportantTrait<{A}>` implemented for `{Self}`",
	label = "My Label",
	note = "Note 1",
	note = "Note 2"
)]
trait ImportantTrait<A> {}

fn use_my_trait(_: impl ImportantTrait<i32>) {}

fn main() {
	use_my_trait(String::new());
}

4.4.2 diagnostic::do_not_recommend属性

  #[diagnostic::do_not_recommend]属性是对编译器的一种提示,让其不要在诊断消息中显式被标注的trait实现。

  如果知道某条建议通常情况下对程序员没有用处,那么抑制该建议可能是有用的。这在涉及宽泛的、覆盖式实现时很常见。该建议可能会把程序员引向错误的路径,或该trait实现可能是一个你不希望暴露的内部细节,又或者程序员可能无法满足其中的约束。#[diagnostic::do_not_recommend]属性可用于告诉编译器不要把该trait实现告诉用户,而只告诉用户该类型没有实现要求的trait。

  该属性应放置在trait实现项上,不过放在其他位置也不算错误。

  该属性不接受任何参数,不过意外的参数不被视为错误。

  在下面的示例中,有一个名为AsExpression的trait,用于将任意类型转换为SQL库中所使用的Expression类型。还有一个名为check的方法,它接受一个AsExpression。

rust 复制代码
pub trait Expression {
	type SqlType;
}

pub trait AsExpression<ST> {
	type Expression: Expression<SqlType = ST>;
}

pub struct Text;
pub struct Integer;

pub struct Bound<T>(T);
pub struct SelectInt;

impl Expression for SelectInt {
	type SqlType = Integer;
}

impl<T> Expression for Bound<T> {
	type SqlType = T;
}

impl AsExpression<Integer> for i32 {
	type Expression = Bound<Integer>;
}

impl AsExpression<Text> for &'_ str {
	type Expression = Bound<Text>;
}

impl<T> Foo for T where T: Expression {}

#[diagnostic::do_not_recommend]
impl<T, ST> AsExpression<ST> for T
where
	T: Expression<SqlType = ST>,
{
	type Expression = T;
}

trait Foo: Expression + Sized {
	fn check<T>(&self, _: T) -> <T as AsExpression<<Self as Expression>::SqlType>>::Expression
	where
		T: AsExpression<Self::SqlType>,
	{
		todo!();
	}
}

fn main() {
	SelectInt.check("bar");
}

  注释掉#[diagnostic::do_not_recommend]:

  SelectInt类型的check方法期望一个Integer类型。用i32类型调用它是可以的,因为它会被AsExpression trait转换为一个Integer。然而,用字符串调用它则不行。

  未添加#[diagnostic::do_not_recommend]的错误消息中包含一段关于&str与Expression之间关系、以及覆盖式实现中未满足的trait约束的、有些令人困惑的消息。添加了之后,编译器不再考虑将该覆盖式实现作为建议。消息应当清晰一些,会指出字符串无法转换为Integer。

5. 代码生成属性

  以下属性用于控制代码生成。

5.1 inline属性

  inline属性提示:被标注函数的代码副本是否应放置在被调用者处,而不是生成对该函数的调用。

  示例:

rust 复制代码
#[inline]
pub fn example1() {}

#[inline(always)]
pub fn example2() {}

#[inline(never)]
pub fn example3() {}

  rustc在看似值得时会对函数进行自动内联。请谨慎使用此属性,因为关于内联的错误决策可能会拖慢程序。

  inline属性的语法为:

复制代码
Syntax
InlineAttribute -> 	inline(always)
					|	inline(never)
					| 	inline

  inline属性只能应用于带函数体的函数------闭包、async块、自由函数、固有实现或trait实现中的关联函数,以及trait定义中具有默认实现的关联函数。

  rustc会忽略在其他位置的使用,但会发出代码检查警告。这将来可能变成错误。

  虽然该属性可以应用于闭包和async块,但其用处有限,因为我们尚不支持表达式上的属性。

rust 复制代码
// 允许语句上的属性
fn main() {
	#[inline] || ();
	#[inline] async {};
}
rust 复制代码
// 尚不允许表达式上的属性
fn main() {
	let f = #[inline] || ();
}

  函数上只有第一次使用inline有效。rustc会对第一次之后的任何使用发出代码检查警告。这将来可能变成错误。

  inline属性支持以下模式:

  #[inline]------提示执行内联展开

  #[inline(always)]------提示应当始终执行内联展开

  #[inline(never)]------提示永远不要执行内联展开

  在每一种形式下,该属性都只是一个提示。编译器可能忽略它。

  当inline应用于trait中的函数时,它只作用于默认实现的代码。

  当inline应用于async函数或async闭包时,它只作用于所生成的poll函数的代码。

  如果函数以no_mangle或export_name进行外部导出,则inline属性会被忽略。

5.2 cold属性

  cold属性提示:被标注函数不太可能被调用,这可能有助于编译器生成更好的代码。

  cold属性使用MetaWord语法。

  cold属性只能应用于带函数体的函数------闭包、async块、自由函数、固有实现或trait实现中的关联函数,以及trait定义中具有默认实现的关联函数。

  rustc会忽略在其他位置的使用,但会发出代码检查警告。这将来可能变成错误。

  虽然该属性可以应用于闭包和async块,但其用处有限,因为我们尚不支持表达式上的属性。

  函数上只有第一次使用cold有效。rustc会对第一次之后的任何使用发出代码检查警告。这将来可能变成错误。

  当cold应用于trait中的函数时,它只作用于默认实现的代码。

5.3 naked属性

  naked属性阻止编译器为被标注函数生成函数序言和尾声------即裸函数。

  示例:

rust 复制代码
fn main() {
	#[cfg(target_arch = "x86_64")]
	{
		// 给给定的数字加上3
		// SAFETY: 函数体遵守sysv64调用约定
		// 维护函数签名,且不会掉落
		#[unsafe(naked)]
		pub extern "sysv64" fn add_n(number: u64) -> u64 {
			core::arch::naked_asm!(
				"add rdi, {}",
				"mov rax, rdi",
				"ret",
				const 3,
			)
		}
	}
}

  naked属性使用MetaWord语法。

  naked属性只能应用于自由函数、固有实现或trait实现中的关联函数,以及trait定义中具有默认实现的关联函数。

  函数上只有第一次使用naked有效。rustc会对第一次之后的任何使用发出代码检查警告。

  naked属性必须用unsafe标记,因为函数体必须遵守函数的调用约定、维护其签名,并且要么返回要么发散(即不能从汇编代码末尾掉落下去)。

  函数体必须恰好由一次naked_sim!宏调用构成。编译器不会为裸函数生成序言或尾声:naked_asm!调用中的汇编代码构成其全部函数体。

  进入函数时,汇编代码可以假设调用栈与寄存器状态符合函数签名与调用约定。

  编译器不得复制汇编代码,除非是在多态函数进行单态化时。这一保证对于定义符号的裸函数很重要。

  unused_variables代码检查在裸函数中被抑制。inline属性不能应用于裸函数。track_caller属性不能应用于裸函数。测试属性(test、ignore、should_panic)不能应用于裸函数。target_feature属性不能应用于裸函数。

  裸函数不能使用Rust ABI。

5.4 no_builtins属性

  no_builtins属性禁用对某些与假定存在的库函数调用相关的代码模式的优化。

  示例:

rust 复制代码
#![no_builtins]

  no_builtins属性使用MetaWord语法。no_builtins属性只能应用于crate根。

  no_builtins属性只有第一次使用有效。rustc会对第一次之后的任何使用发出代码检查警告。

5.5 target_feature属性

  target_feature属性可以应用于函数,以针对特定的平台架构特性启用该函数的代码生成。它使用MetaListNameValueStr语法,其唯一键为enable,值为以逗号分隔的待启用特性名称字符串。

rust 复制代码
#[cfg(target_feature = "avx2")]
#[target_feature(enable = "avx2")]
fn foo_avx2() {}

  每个目标架构都有一组可以启用的特性。为crate未编译的目标架构指定特性是错误的。

  在带target_feature注解的函数内部定义的闭包会从外部函数继承该属性。

  调用一个使用当前运行平台不支持的编译特性的函数是未定义行为,除非平台明确记载这是安全的。

  除非下方平台规则另有规定,否则适用以下限制:

  只有调用者自身也启用了被调用者所需的全部target_feature时,才能在安全代码中调用安全的#[target_feature]函数以及继承该属性的闭包;在unsafe上下文中此限制不适用。

  安全的#[target_feature]函数以及继承该属性的闭包只能在其所需全部target_feature均已启用的上下文中被强转为安全的函数指针;此限制不适用于unsafe函数指针。

  隐式启用的特性也包含在此规则之内。例如,一个sse2函数可以调用标记为sse的函数。

rust 复制代码
fn main() {
	#[cfg(target_feature = "sse2")]
	{
		#[target_feature(enable = "sse")]
		fn foo_sse() {}

		fn bar() {
			// 在这里调用foo_sse是unsafe的,因为我们必须先确保SSE可用
			// 即使sse在目标平台上是默认启用,或已通过编译器标志手动启用
			unsafe {
				foo_sse();
			}
		}

		#[target_feature(enable = "sse")]
		fn bar_sse() {
			// 在这里调用foo_sse是安全的
			foo_sse();
			|| foo_sse();
		}

		#[target_feature(enable = "sse2")]
		fn bar_sse2() {
			// 在这里调用foo_sse是安全的,因为sse2蕴含sse
			foo_sse();
		}
	}
}

  带#[target_feature]属性的函数永远不实现Fn系列trait,尽管从外层函数继承特性的闭包可以实现。

  #[target_feature]属性不允许出现在以下位置:

  main函数

  panic_handler函数

  安全的trait方法

  trait中的安全默认函数

  标记了target_feature的函数不会被内联到不支持给定特性的上下文中。#[inline(always)]属性不能与target_feature属性同时使用。

5.5.1 可用特性

  以下是可用特性名称的列表。

  在本列表中标记为cfg only的目标特性名称只能用于target_feature条件编译选项,而不能用于target_feature属性。

5.5.1.1 x86或x86_64

  在此平台上执行带有不支持特性的代码是未定义行为。因此,在此平台上使用#[target_feature]函数遵循上述限制。

特性 隐式启用 描述
adx ADX------多精度加法进位指令扩展
aes sse2 AES------高级加密标准
avx sse4.2 AVX------高级矢量扩展
avx2 avx AVX2------高级矢量扩展2
avx512bf16 avx512bw AVX512-BF16------512位高级矢量扩展-Bfloat16扩展
avx512bitalg avx512bw AVX512-BITALG------512位高级矢量扩展-位算法
avx512bw avx512f AVX512-BW------512位高级矢量扩展-字节与字指令
avx512cd avx512f AVX512-CD------512位高级矢量扩展-冲突检测指令
avx512dq avx512f AVX512-DQ------512位高级矢量扩展-双字与四字指令
avx512f avx2、fma、f16c AVX512-F------512位高级矢量扩展-基础
avx512fp16 avx512bw AVX512-FP16------512位高级矢量扩展-Float16扩展
avx512ifma avx512f AVX512-IFMA------512位高级矢量扩展-整数融合乘加
avx512vbmi avx512bw AVX512-VBMI------512位高级矢量扩展-矢量字节操作指令
avx512vbmi2 avx512bw AVX512-VBMI2------512位高级矢量扩展-矢量字节操作指令2
avx512vl avx512f AVX512-VL------512位高级矢量扩展-矢量长度扩展
avx512vnni avx512f AVX512-VNNI------512位高级矢量扩展-矢量神经网络指令
avx512vp2intersect avx512f AVX512-VP2INTERSECT------512位高级矢量扩展-矢量对交集到掩码寄存器对
avx512vpopcntdq avx512f AVX512-VPOPCNTDQ------512位高级矢量扩展-矢量种群计数指令
avxifma avx2 AVX-IFMA------高级矢量扩展-整数融合乘加
avxneconvert avx2 AVX-NE-CONVERT------高级矢量扩展-无异常浮点转换指令
avxvnni avx2 AVX-VNNI------高级矢量扩展-矢量神经网络指令
avxvnniint16 avx2 AVX-VNNI-INT16------高级矢量扩展-16位整数矢量神经网络指令
avxvnniint8 avx2 AVX-VNNI-INT8------高级矢量扩展-8位整数矢量神经网络指令
bmi1 BMI1------位操作指令集
bmi2 BMI2------位操作指令集2
cmpxchg16b cmpxchg16b------原子地比较并交换16字节数据
f16c avx F16C------16位浮点转换指令
fma avx FMA3------三操作数融合乘加
fxsr fxsave与fxrstor------保存并恢复x87 FPU、MMX技术与SSE状态
gfni sse2 GFNI------伽罗瓦域新指令
kl sse2 KEYLOCKER------英特尔密钥锁存指令
lzcnt lzcnt------前导零计数
movbe movbe------交换字节序后移动数据
pclmulqdq sse2 pclmulqdq------打包无进位乘法四字
popcnt popcnt------置位为1的位数计数
rdrand rdrand------读取随机数
rdseed rdseed------读取随机种子
sha sse2 SHA------安全哈希算法
sha512 avx2 SHA512------512位摘要安全哈希算法
sm3 avx SM3------商密3哈希算法
sm4 avx2 SM4------商密4分组密码算法
sse SSE------流式SIMD扩展
sse2 sse SSE2------流式SIMD扩展2
sse3 sse2 SSE3------流式SIMD扩展3
sse4.1 sse3 SSE4.1------流式SIMD扩展4.1
sse4.2 sse4.1 SSE4.2------流式SIMD扩展4.2
sse4a sse3 SSE4a------流式SIMD扩展4a
ssse3 sse3 SSSE3------补充流式SIMD扩展3
tbm TBM------尾部位操作
vaes avx2、aes VAES------矢量AES指令
vpclmulqdq avx、pclmulqdq VPCLMULQDQ------四字矢量无进位乘法
widekl kl KEYLOCKER_WIDE------英特尔宽密钥锁存指令
xsave xsave------保存处理器扩展状态
xsavec xsavec------压缩保存处理器扩展状态
xsaveopt xsaveopt------优化保存处理器扩展状态
xsaves xsaves------保存处理器扩展状态(监管状态)
5.5.1.2 aarch64

  在此平台上使用#[target_feature]函数遵循上述限制。这些特性的进一步文档可在ARM架构参考手册或developer.arm.com上的其他位置找到。

  以下特性对如果使用,应同时标记为启用或禁用:paca与pacg,LLVM目前将它们实现为同一特性。

特性 隐式启用 特性名称
aes neon FEAT_AES与FEAT_PMULL------高级SIMD AES与PMULL指令
bf16 FEAT_BF16------BFloat16指令
bti FEAT_BTI------分支目标识别
crc FEAT_CRC------CRC32校验和指令
dit FEAT_DIT------数据无关时序指令
dotprod neon FEAT_DotProd------高级SIMD int8点积指令
dpb FEAT_DPB------数据缓存清洗至持久点
dpb2 dpb FEAT_DPB2------数据缓存清洗至深度持久点
f32mm sve FEAT_F32MM------SVE单精度浮点矩阵乘法指令
f64mm sve FEAT_F64MM------SVE双精度浮点矩阵乘法指令
fcma neon FEAT_FCMA------浮点复数支持
fhm fp16 FEAT_FHM------半精度FP FMLAL指令
flagm FEAT_FLAGM------条件标志操作
fp16 neon FEAT_FP16------半精度FP数据处理
frintts FEAT_FRINTTS------浮点转整数辅助指令
i8mm FEAT_I8MM------int8矩阵乘法
jsconv neon FEAT_JSCVT------JavaScript转换指令
lor FEAT_LOR------受限排序区域扩展
lse FEAT_LSE------大型系统扩展
mte FEAT_MTE与FEAT_MTE2------内存标签扩展
neon FEAT_AdvSimd与FEAT_FP------浮点与高级SIMD扩展
paca FEAT_PAUTH------指针认证(地址认证)
pacg FEAT_PAUTH------指针认证(地址认证)
pan FEAT_PAN------特权访问永不扩展
pmuv3 FEAT_PMUv3------性能监视器扩展v3
rand FEAT_RNG------随机数生成器
ras FEAT_RAS与FEAT_RASv1p1------可靠性、可用性与可服务性扩展
rcpc FEAT_LRCPC------释放一致性处理器一致性
rcpc2 rcpc FEAT_LRCPC2------带立即数偏移的RcPc
rdm neon FEAT_RDM------舍入双乘加
sb FEAT_SB------推测屏障
sha2 neon FEAT_SHA1与FEAT_SHA256------高级SIMD SHA指令
sha3 sha2 FEAT_SHA512与FEAT_SHA3------高级SIMD SHA指令
sm4 neon FEAT_SM3与FEAT_SM4------高级SIMD SM3/4指令
spe FEAT_SPE------统计剖析扩展
ssbs FEAT_SSBS与FEAT_SSBS2------推测存储绕过安全
sve neon FEAT_SVE------可伸缩矢量扩展
sve2 sve FEAT_SVE2------可伸缩矢量扩展2
sve2-aes sve2、aes FEAT_SVE_AES与FEAT_SVE_PMULL128------SVE AES指令
sve2-bitperm sve2 FEAT_SVE2_BitPerm------SVE位置换
sve2-sha3 sve2、sha3 FEAT_SVE2_SHA3------SVE SHA3指令
sve2-sm4 sve2、sm4 FEAT_SVE2_SM4------SVE SM4指令
tme FEAT_TME------事务内存扩展
vh FEAT_VHE------虚拟化宿主扩展
5.5.1.3 loongarch

  在此平台上使用#[target_feature]函数遵循上述限制。

特性 隐式启用 描述
f F------单精度浮点指令
d f D------双精度浮点指令
frecipe FRECIPE------倒数近似指令
lasx lsx LASX------256位矢量指令
lbt LBT------二进制翻译指令
lsx d LSX------128位矢量指令
lvz LVZ------虚拟化指令
div32 DIV32------接受非符号扩展32位操作数的除法指令
lam-bh LAM-BH------字节与半字的原子交换和加法指令
lamcas LAMCAS------字节、半字、字与双字的原子比较并交换指令
ld-seq-sa LD-SEQ-SA------对同一地址的加载操作的顺序排序
scq SCQ------条件存储四字指令
5.5.1.4 riscv32或riscv64

  在此平台上使用#[target_feature]函数遵循上述限制。这些特性的进一步文档可在它们各自的规范中找到。许多规范描述在RISC-V ISA手册(20250508版)中,或在托管于RISC-V Github账号上的其他手册中。

特性 隐式启用 描述
a zaamo、zalrsc A------原子指令
b zba、zbc、zbs B------位操作指令
c zca C------压缩指令
d f D------(cfg only)双精度浮点
e E------(cfg only)含16个通用寄存器的嵌入式指令集
f zicsr F------(cfg only)单精度浮点
m M------整数乘法与除法指令
za64rs za128rs Za64rs------平台行为:长度小于等于64字节的自然对齐保留集
za128rs Za128rs------平台行为:长度小于等于128字节的自然对齐保留集
zaamo Zaamo------原子内存操作指令
zabha zaamo Zabha------字节与半字原子内存操作指令
zacas zaamo Zacas------原子比较并交换(CAS)指令
zalrsc Zalrsc------保留加载/条件存储指令
zama16b Zama16b------平台行为:对不跨越自然对齐16字节边界的主内存区域进行非对齐加载、存储和AMO是原子的
zawrs Zawrs------保留集等待指令
zba Zba------地址生成指令
zbb Zbb------基础位操作
zbc zbkc Zbc------无进位乘法
zbkb Zbkb------密码学位操作指令
zbkc Zbkc------密码学无进位乘法
zbkx Zbkx------交叉开关置换
zbs Zbs------单比特指令
zca Zca------压缩指令:整数部分子集
zcb zca Zcb------节省代码大小的简单压缩指令
zcmop zca Zcmop------压缩可能操作
zic64b Zic64b------平台行为:自然对齐的64字节缓存块
zicbom Zicbom------缓存块管理指令
zicbop Zicbop------缓存块预取提示指令
zicboz Zicboz------缓存块清零指令
ziccamoa Ziccamoa------平台行为:可缓存且一致的主内存支持所有基本原子操作
ziccif Ziccif------平台行为:可缓存且一致的主内存支持指令获取,且对最大为min(ILEN, XLEN)的自然对齐2的幂大小获取是原子的
zicclsm Zicclsm------平台行为:可缓存且一致的主内存支持非对齐加载/存储访问
ziccrse Ziccrse------平台行为:可缓存且一致的主内存保证LR/SC序列最终成功
zicntr zicsr Zicntr------基础计数器与定时器
zicond Zicond------整数条件操作指令
zicsr Zicsr------控制与状态寄存器(CSR)指令
zifencei Zifencei------指令获取栅栏指令
zihintntl Zihintntl------非时序局部性提示指令
zihintpause Zihintpause------暂停提示指令
zihpm zicsr Zihpm------硬件性能计数器
zimop Zimop------可能操作
zk zkn、zkr、zks、zkt、zbkb、zbkc、zbkx Zk------标量密码学
zkn zknd、zkne、zknh、zbkb、zbkc、zbkx Zkn------NIST算法套件扩展
zknd Zknd------NIST套件:AES解密
zkne Zkne------NIST套件:AES加密
zknh Zknh------NIST套件:哈希函数指令
zkr Zkr------熵源扩展
zks zksed、zksh、zbkb、zbkc、zbkx Zks------商密算法套件
zksed Zksed------商密套件:SM4分组密码指令
zksh Zksh------商密套件:SM3哈希函数指令
zkt Zkt------数据无关执行延迟子集
ztso Ztso------全存储排序
5.5.1.5 wasm32或wasm64

  在Wasm平台上,安全的#[target_feature]函数可以始终在安全上下文中使用。通过#[target_feature]属性不可能引起未定义行为,因为尝试使用wasm引擎不支持的指令会在加载时失败,而不会被以不同于编译器预期的方式解释的风险。

特性 隐式启用 描述
bulk-memory WebAssembly批量内存操作提案
extended-const WebAssembly扩展常量表达式提案
mutable-globals WebAssembly可变全局变量提案
nontrapping-fptoint WebAssembly非捕获浮点转整数转换提案
relaxed-simd simd128 WebAssembly宽松simd提案
sign-ext WebAssembly符号扩展运算符提案
simd128 WebAssembly simd提案
multivalue WebAssembly多值提案
reference-types WebAssembly引用类型提案
tail-call WebAssembly尾调用提案
5.5.1.6 s390x

  在s390x目标上,使用带#[target_feature]属性的函数遵循上述限制。这些特性的进一步文档可在《z/Architecture操作原理》第1章的z/Architecture增补一节中找到。

特性 隐式启用 描述
vector 128位矢量
vector-enhancements-1 vector 矢量增强1
vector-enhancements-2 vector-enhancements-1 矢量增强2
vector-enhancements-3 vector-enhancements-2 矢量增强3
vector-packed-decimal vector 矢量压缩十进制
vector-packed-decimal-enhancement vector-packed-decimal 矢量压缩十进制增强
vector-packed-decimal-enhancement-2 vector-packed-decimal-enhancement 矢量压缩十进制增强2
vector-packed-decimal-enhancement-3 vector-packed-decimal-enhancement-2 矢量压缩十进制增强3
nnp-assist vector nnp辅助
miscellaneous-extensions-2 杂项扩展2
miscellaneous-extensions-3 杂项扩展3
miscellaneous-extensions-4 杂项扩展4

5.5.2 附加信息

  请参阅target_feature条件编译选项,它根据编译时设置选择性启用或禁用代码的编译。请注意,该选项不受target_feature属性影响,只由整个crate启用的特性驱动。

  某个特性是否已启用可以在运行时使用标准库中平台特定的宏来检查,例如is_x86_feature_detected或is_aarch64_feature_detected。

  rustc为每个目标和CPU都有一组默认启用的特性。CPU可以使用-C target-cpu标志选择。单个特性可以使用-C target-feature标志为整个crate启用或禁用。

5.6 track_caller属性

  track_caller属性可以应用于任何带Rust ABI的函数,但入口点fn main除外。

  当应用于trait声明中的函数和方法时,该属性适用于所有实现。如果trait提供了带属性的默认实现,则该属性也适用于覆写实现。

  当应用于外部块中的函数时,该属性也必须应用于任何链接的实现,否则会导致未定义行为。当应用于一个对外部块可用的函数时,外部块中的声明也必须具有该属性,否则会导致未定义行为。

5.6.1 行为

  将属性应用于函数f,允许f内的代码获得一个提示,了解导致f被调用的最顶层被跟踪调用的Location。在观察点上,实现的表现为:它从f的帧向上遍历调用栈,找到最近的未标注属性的函数outer的帧,并返回outer中被跟踪调用的Location。

rust 复制代码
#[track_caller]
fn f() {
	println!("{}", std::panic::Location::caller());
}

  core提供core::panic::Location::caller用于观察调用者位置。它包装了由rustc实现的core::intrinsics::caller_location内建。

  因为得到的Location只是一个提示,实现可能提前停止其向上遍历栈的过程。

  当f被calls_f直接调用时,f内的代码观察到的时calls_f内的调用点:

rust 复制代码
#[track_caller]
fn f() {
	println!("{}", std::panic::Location::caller());
}

fn calls_f() {
	f(); // <--- f()打印此位置
}

fn main() {
	calls_f();
}

  当f被另一个标注了属性的函数g调用,而g又被calls_g调用,f与g内的代码观察到的都是g在calls_g内的调用点:

rust 复制代码
#[track_caller]
fn f() {
	println!("{}", std::panic::Location::caller());
}

#[track_caller]
fn g() {
	println!("{}", std::panic::Location::caller());
	f();
}

fn calls_g() {
	g(); // <--- g()打印此位置两次:一次来自自身,一次来自f()
}

fn main() {
	calls_g();
}

5.6.2 局限性

  此信息只是一个提示,实现不需要保留它。

  特别是,将带有#[track_caller]的函数强转为函数指针会创建一个垫片,在观察者看来,它似乎是在被标注函数的定义点处被调用的,从而在虚拟调用中丢失真实的调用者信息。这种强转的一个常见例子是创建其方法被标注属性的trait对象。

  上述函数指针的垫片是必要的,因为rustc在代码生成上下文中通过在函数ABI上附加一个隐式参数来实现track_caller,但对于间接调用这会是健全性问题,因为该参数不是函数类型的一部分,并且给定的函数指针类型可能指向也可能不指向带该属性的函数。垫片会向函数指针的调用者隐藏该隐式参数,从而保持健全性。

5.7 instruction_set属性

  instruction_set属性指定函数在代码生成期间将使用的指令集。这允许在单个程序中混用多个指令集。

rust 复制代码
#[instruction_set(arm::a32)]
fn arm_code() {}

#[instruction_set(arm::t32)]
fn thumb_code() {}

  instruction_set属性使用MetaListPaths语法来指定一个由架构名称与指令集名称组成的单一路径。

  instruction_set属性只能应用于带函数体的函数------闭包、async块、自由函数、固有实现或trait实现中的关联函数,以及trait定义中具有默认实现的关联函数。

  rustc会忽略在其他位置的使用,但会发出代码检查警告。这将来可能变成错误。

  虽然该属性可以应用于闭包和async块,但其用处有限,因为我们尚不支持表达式上的属性。

  instruction_set属性在一个函数上只能使用一次。  instruction_set属性只能用于支持给定值的指令集的目标。

  使用instruction_set属性时,函数中的任何内联汇编都必须使用指定的指令集,而不是目标默认指令集。

5.7.1 ARM上的instruction_set

  当目标是ARMv4T与ARMv5te架构时,instruction_set支持的值为:

  arm::a32------将函数生成为A32 ARM代码

  arm::t32------将函数生成为T32 Thumb代码

  如果函数的地址被取为函数指针,地址的低位将取决于所选的指令集:

  对于arm::a32 ARM,它将为0

  对于arm::t32 Thumb,它将为1

6. 限制属性

  以下属性影响编译期限制。

6.1 recursion_limit属性

  recursion_limit属性可以在crate级别应用,用于设置可能无限递归的编译期操作(如宏展开或自动解引用)的最大深度。

  它使用MetaNameValueStr语法来指定递归深度。rustc中默认值为128。

rust 复制代码
#![recursion_limit = "4"]

macro_rules! a {
	() => { a!(1); };
	(1) => { a!(2); };
	(2) => { a!(3); };
	(3) => { a!(4); };
	(4) => { };
}

fn main() {
	// 这个宏无法展开,因为它需要的递归深度大于4
	a!();
}
rust 复制代码
#![recursion_limit = "1"]

fn main() {
	// 这个代码无法通过编译,因为自动解引用需要两步递归
	(|_: &u8| {})(&&&1);
}

6.2 type_length_limit属性

  type_length_limit属性设置在单态化期间构造具体类型时允许的类型替换最大数量。

  rustc只在nightly的-Zenforce-type-length-limit标志激活时才强制执行该限制。

rust 复制代码
#![type_length_limit = "4"]

fn f<T>(x: T) {}

// 这段代码无法通过编译,因为单态化
// 为f::<((((i32,),i32),i32),i32)>需要的类型元素超过4个
fn main() {
	f(((((1,), 2), 3), 4));
}

  rustc中的默认值是1048576。

  type_length_limit属性使用MetaNameValueStr语法。字符串中的值必须是一个非负数。

  type_length_limit属性只能应用于crate根。rustc会忽略在其他位置的使用,但会发出代码检查警告。这将来可能变成错误。

  在一个项上只有第一次使用type_length_limit有效。rustc会对第一次之后的任何使用发出代码检查警告。这将来可能变成错误。

7. 类型系统属性

  以下属性用于改变类型的使用方式。

7.1 non_exhaustive属性

  non_exhaustive属性表示一个类型或变体可能在未来增加更多字段或变体。

  它可以应用于结构体、枚举和枚举变体。

  non_exhaustive属性使用MetaWord语法。

  在定义它的crate内部,non_exhaustive没有任何效果。

rust 复制代码
#[non_exhaustive]
pub struct Config {
	pub window_width: u16,
	pub window_height: u16,
}

#[non_exhaustive]
pub struct Token;

#[non_exhaustive]
pub struct Id(pub u64);

#[non_exhaustive]
pub enum Error {
	Message(String),
	Other,
}

pub enum Message {
	#[non_exhaustive]
	Send { from: u32, to: u32, contents: String },
	#[non_exhaustive]
	Reaction(u32),
	#[non_exhaustive]
	Quit,
}

fn main() {
	// 在定义crate内部,非穷尽结构体可以照常构造
	let config = Config { window_width: 640, window_height: 480 };
	let token = Token;
	let id = Id(4);

	// 在定义crate内部,非穷尽结构体可以被穷尽地匹配。
	let Config { window_width, window_height } = config;
	let Token = token;
	let Id(id_number) = id;
	
	let error = Error::Other;
	let message = Message::Reaction(3);

	// 在定义crate内部,非穷尽枚举可以被穷尽地匹配
	match &error {
		Error::Message(s) => (),
		Error::Other => (),
	}
	
	match message {
		// 在定义crate内部,非穷尽变体可以被穷尽地匹配
		Message::Send { from, to, contents } => (),
		Message::Reaction(id) => (),
		Message::Quit => (),
	}
}

  在定义crate之外,标注了non_exhaustive的类型存在一些限制,这些限制在新增字段或变体时保持向后兼容性。

  在定义crate之外,不能构造非穷尽类型。

  非穷尽变体(结构体变体或枚举变体)不能使用结构体表达式构造,包括使用函数式更新语法。

  单元结构体隐式定义的同名常量,或元组结构体结构体的同名构造函数,其可见性不高于pub(crate)。也就是说,如果结构体的可见性是pub,那么该常量或构造器的可见性是pub(crate);否则两个项的可见性相同。

  以下构造示例在定义crate之外无法通过编译:

rust 复制代码
use other::{Config, Token, Id, Error, Message};

fn main() {
	// 不能构造Config的实例:如果other的新版本添加了新字段
	// 这段代码就会编译失败,因此是不允许的
	let config = Config { window_width: 640, window_height: 480 };
	// 不能构造Token的实例:如果添加了新字段,它就不再是单元结构体了
	// 因此有单元结构体创建的同名常量在crate外部不公开;这段代码编译失败
	let token = Token;
	// 不能构造Id的实例:如果添加了新字段,它的构造器函数签名就会改变
	// 因此它的构造器函数在crate外部不公开,这段代码编译失败
	let id = Id(5);
	// 可以构造Error的实例:引入新变体不会导致这段代码编译失败
	let error = Error::Message("foo".to_string());
	// 不能构造Message::Send或Message::Reaction的实例:
	// 如果other的新版本添加了新字段,这段代码就会编译失败,因此是不允许的
	let message = Message::Send { from: 0, to: 1, contents: "foo".to_string(), };
	let message = Message::Reaction(0);

	// 不能构造Message::Quit的实例:如果它在other中被转换为元组枚举变体,这段代码就会编译失败
	let message = Message::Quit;
}

  在定义crate之外,对非穷尽类型进行模式匹配存在以下限制:

  当对非穷尽变体(结构体变体或枚举变体)进行模式匹配时,必须使用结构体模式,且必须包含...。元组枚举变体的构造器的可见性被降低为不高于pub(crate)。

  当对非穷尽枚举进行模式匹配时,匹配某个变体不会对分支的穷尽性做出贡献。以下匹配示例在定义crate之外无法通过编译:

rust 复制代码
// 无法运行,伪代码,因为非穷尽类型没有提供构造函数
use other::{Config, Token, Id, Error, Message};

fn main() {
	// 没有通配分支就无法匹配非穷尽枚举。
	match &error {
		Error::Message(s) => (),
		Error::Other => (),
		// 加上 _ => (), 才能通过编译
	}
	// 没有通配就无法匹配非穷尽结构体
	if let Ok(Config { window_width, window_height }) = config {
		// 加上 .. 才能通过编译
	}

	// 出使用带通配的花括号结构体语法之外,不能匹配非穷尽的单元结构体或元组结构体
	// 写成 let Token { .. } = token; 才能通过编译
	let Token = token;
	// 写成 let Id { 0: id_number, .. } = id; 才能通过编译
	let Id(id_number) = id;

	match message {
		// 没有通配就无法匹配非穷尽的结构体枚举变体
		Message::Send { from, to, contents } => (),
		// 不能匹配非穷尽的元组或单元枚举变体
		Message::Reaction(type) => (),
		Message::Quit => (),
	}
}

  同样,也不允许对包含任何非穷尽变体的枚举使用数值类型转换as。例如,下面的枚举可以转换,因为它不包含任何非穷尽变体:

rust 复制代码
#[non_exhaustive]
pub enum Example {
	First,
	Second,
}

  但是,如果枚举即使只包含一个非穷尽变体,转换也会报错。考虑该枚举的以下修改版本:

rust 复制代码
#[non_exhaustive]
pub enum EnumWithNonExhaustiveVariants {
	First,
	#[non_exhaustive]
	Second,
}
rust 复制代码
use other::EnumWithNonExhaustiveVariants;

fn main() {
	// 错误:当枚举定义在另一个crate中时,不能对含有非穷尽变体的枚举进行转换
	let _ = EnumWithNonExhaustiveVariants::First as u8;
}

  在下游crate中,非穷尽类型总是被视为可居住(至少有一个值存在)的。

8. 调试器属性

  以下属性用于在使用GDB或WinDbg等第三方调试器时增强调试体验。

8.1 debugger_visualizer属性

  debugger_visualizer属性可用于将调试器可视化器文件嵌入调试信息中。这在显示值时能改善调试器体验。

rust 复制代码
#![debugger_visualizer(natvis_file = "Example.natvis")]
#![debugger_visualizer(gdb_script_file = "example.py")]

  debugger_visualizer属性使用MetaListNameValueStr语法来指定其输入。必须指定以下键之一:

  natvis_file

  gdb_script_file

  debugger_visualizer属性只能应用于模块或crate根。

  debugger_visualizer属性在一个形式上可以使用任意次数。所有指定的可视化器文件都会被加载。

8.1.1 与Natvis一起使用

  Natvis是面向Microsoft调试器(如Visual Studio和WinDbg)的、基于XML的框架,它使用声明式规则来自定义类型的显示。关于Natvis格式的详细信息,请参阅Microsoft的Natvis文档。

  该属性只支持在-windows-msvc目标上嵌入Natvis文件。

  Natvis文件的路径通过natvis_file键指定,它是相对于源文件的路径。

rust 复制代码
#![debugger_visualizer(natvis_file = "Rectangle.natvis")]

struct FancyRect {
	x: f32,
	y: f32,
	dx: f32,
	dy: f32,
}

fn main() {
	let fancy_rect = FancyRect { x: 10.0, y: 10.0, dx: 5.0, dy: 5.0 };
	println!("set breakpoint here");
}

  Rectangle.natvis包含:

xml 复制代码
<?xml version="1.0" encoding="utf-8"?>
<AutoVisualizer xmlns="http://schemas.microsoft.com/vstudio/debugger/natvis/2010">
    <Type Name="foo::FancyRect">
      <DisplayString>({x},{y}) + ({dx}, {dy})</DisplayString>
      <Expand>
        <Synthetic Name="LowerLeft">
          <DisplayString>({x}, {y})</DisplayString>
        </Synthetic>
        <Synthetic Name="UpperLeft">
          <DisplayString>({x}, {y + dy})</DisplayString>
        </Synthetic>
        <Synthetic Name="UpperRight">
          <DisplayString>({x + dx}, {y + dy})</DisplayString>
        </Synthetic>
        <Synthetic Name="LowerRight">
          <DisplayString>({x + dx}, {y})</DisplayString>
        </Synthetic>
      </Expand>
    </Type>
</AutoVisualizer>

  首先cargo build构建Rust源文件的可执行文件。然后在WinDbg中打开这个可执行文件。

  在命令窗口输入bp `learning!D:\...\main.rs:12`,这里learning是可执行文件的名称,后面的Rust源文件的路径,12是行号。这条命令的目的是设置断点。

  在命令窗口输入g。运行至端点处。

  在命令窗口输入dx fancy_rect,查看变量调试输出。

8.1.2 与GDB一起使用

  GDB支持使用结构化的Python脚本,称为美化打印器,它描述类型在调试器视图中应如何展示。关于美化打印器的详细信息,请参阅GDB的美化打印文档。

  在GDB下调试二进制文件时,嵌入的美化打印器不会被自动加载。有两种方式可以启用嵌入美化打印器的自动加载:

  1. 使用额外参数启动GDB,显式地将目录或二进制文件添加到自动加载安全路径:gdb -iex "add-auto-load-safe-path safe-path path/to/binary" path/to/binary。更多信息请参阅GDB的自动加载文档。

  2. 在$HOME/.config/gdb下创建一个名为gdbinit的文件(目录不存在,手动创建)。在该文件中添加以下一行:add-auto-load-safe-path path/to/binary。

  这些脚本通过gdb_script_file键嵌入,它是相对于源文件的路径。

rust 复制代码
#![debugger_visualizer(gdb_script_file = "printer.py")]

struct Person {
	name: String,
	age: i32,
}

fn main() {
	let bob = Person { name: "Bob".to_string(), age: 10 };
	println!("set breakpoint here");
}

  printer.py包含:

python 复制代码
import gdb

class PersonPrinter:
	"Print a Person"

	def __init__(self, val):
		self.val = val
		self.name = val["name"]
		self.age = int(val["age"])

	def to_string(self):
		return "{} is {} years old.".format(self.name, self.age)

def lookup(val):
	lookup_tag = val.type.tag
	if lookup_tag is None:
		return None
	if "foo::Preson" == lookup_tag:
		return PersonPrinter(val)
	
	return None

gdb.current_objfile().pretty_printers.append(lookup)

  当crate的调试可执行文件被传入GDB时,print bob将显示:

复制代码
"Bob" is 10 years old.

8.2 collapse_debuginfo属性

  collapse_debuginfo属性控制:在为调用某个宏的代码生成调试信息时,来自该宏定义的代码位置是否被折叠为与该宏调用点相关联的单个位置。

rust 复制代码
#[collapse_debuginfo(yes)]
macro_rules! example {
	() => {
		println!("hello!");
	};
}

  使用调试器时,调用example宏看起来可能像是在调用一个函数。也就是说,当单步执行到调用点时,它可能显示宏调用,而不是展开后的代码。

  collapse_debuginfo属性的语法为:

复制代码
Syntax
CollapseDebuginfoAttribute -> 	collapse_debuginfo(yes)
							|	collapse_debuginfo(no)
							| 	collapse_debuginfo(external)

  collapse_debuginfo属性只能应用于macro_rules定义。

  collapse_debuginfo属性在一个宏上只能使用一次。

  collapse_debuginfo属性接受以下选项:

  #[collapse_debuginfo(yes)]------调试信息中的代码位置被折叠

  #[collapse_debuginfo(no)]------调试信息中的代码位置不被折叠

  #[collapse_debuginfo(external)]------只有当宏来自不同的crate时,调试信息中的代码位置才被折叠

  external行为是没有该属性的宏的默认行为,除非它们是内建宏。对于内建宏,默认值是yes。

  rustc有一个-C collapse-macro-debuginfo命令行选项,可以覆盖默认行为以及任何#[collapse_debuginfo]属性的值。

参考

1、属性

相关推荐
滕州市燕猫虎计算机科技工作室个体工商户11 小时前
《Rust程序设计》学习笔记六:错误处理
rust·学习笔记·rust程序设计·rust学习笔记
达子66614 小时前
RUST 图解 第 3 章:通用编程概念(Common Programming Concepts)
开发语言·算法·rust
对象存储与RustFS1 天前
HelixDB 上手:Agent 记忆新方案,基于 RustFS 对象存储,附完整 docker-compose 部署示例
后端·rust·开源
Amos_Web1 天前
Rspack 源码解析(二十):Typed Hook 系统如何靠宏生成
前端·rust·前端框架
Source.Liu1 天前
【A11】拆分前端,实现多页面方案
前端·rust
Kapaseker1 天前
别只会 unwrap():Rust Result 的几种实用写法
rust
DongQiShanRen2 天前
玄龙(上):TICK 主循环——意识心跳怎么跳
linux·jvm·数据库·人工智能·数据挖掘·rust
编程序的员2 天前
使用OpenTelemetry来监控Rust应用
运维·rust·monitoring·opentelemetry
对象存储与RustFS2 天前
JuiceFS + 对象存储:把 S3 变成 POSIX 文件系统实测
后端·rust·开源