Rust Attribute(属性标记)完整整理
- [一、Rust Attribute](#一、Rust Attribute)
-
- [1、什么是 Attribute](#1、什么是 Attribute)
- [2、Attribute 的语法结构](#2、Attribute 的语法结构)
- 3、按用途分类整理
-
- [3.1、 条件编译类](#3.1、 条件编译类)
- [3.2、 代码生成类](#3.2、 代码生成类)
- [3.3、 lint 控制类](#3.3、 lint 控制类)
- [3.4 、文档与注释类](#3.4 、文档与注释类)
- [3.5、 模块与 crate 配置类](#3.5、 模块与 crate 配置类)
- [3.6 、函数与调用约定类](#3.6 、函数与调用约定类)
- [3.7、 外部接口与 ABI 类](#3.7、 外部接口与 ABI 类)
- [3.8 、异步与运行时类](#3.8 、异步与运行时类)
- 4、常用组合实战
-
- [4.1 、序列化场景](#4.1 、序列化场景)
- [4.2、 条件编译 + 派生组合](#4.2、 条件编译 + 派生组合)
- [4.3、 测试模块完整示例](#4.3、 测试模块完整示例)
- [5、 常见问题与注意事项](#5、 常见问题与注意事项)
-
- [5.1、 外部属性与内部属性的区别](#5.1、 外部属性与内部属性的区别)
- [5.2、 属性参数写法](#5.2、 属性参数写法)
- [5.3 、自定义属性](#5.3 、自定义属性)
- 6、总结
- 二、代码示例
一、Rust Attribute
1、什么是 Attribute
Attribute(属性标记)是 Rust 中用于给代码附加元数据的机制,它可以在编译期影响代码的行为、生成额外代码、开启 lint 检查或关闭警告。Attribute 以 # 开头,分为两种形式:
- 外部属性 :
#[...],作用于其后的下一个项(函数、结构体、模块等)。
- 内部属性 :
#![...],作用于其所在的整个容器(如整个文件、整个模块或整个函数)。
// 外部属性:作用于下面的函数
#[allow(dead_code)]
fn unused_fn() {}
// 内部属性:作用于整个文件(通常写在文件开头)
#![allow(dead_code)]
2、Attribute 的语法结构
一个完整的 Attribute 由以下几部分组成:
# [ 路径 ( 参数 ) ]
- 路径 :属性名,如
derive、cfg、allow。
- 参数:可选,可以是名称、值、字符串或嵌套的属性列表。
常见写法示例:
// 无参数
#[inline]
// 带一个名称参数
#[cfg(test)]
// 带一个字符串参数
#[doc = "这是文档"]
// 带多个参数
#[allow(dead_code, unused_variables)]
// 带键值对参数
#[deprecated(since = "1.10.0", note = "请使用新函数")]
3、按用途分类整理
3.1、 条件编译类
| 属性 |
作用 |
#[cfg(...)] |
按条件编译代码,常用于区分操作系统、架构或特性开关 |
#[cfg_attr(...)] |
当条件满足时再应用另一个属性 |
// 仅在 Linux 下编译
#[cfg(target_os = "linux")]
fn linux_only() {}
// 仅在测试模式下编译
#[cfg(test)]
mod tests {}
// 条件满足时自动派生 Debug
#[cfg_attr(feature = "serde", derive(Serialize))]
struct Config;
3.2、 代码生成类
| 属性 |
作用 |
#[derive(...)] |
自动为类型实现指定的 trait(如 Debug、Clone、PartialEq) |
#[inline] |
建议编译器内联该函数 |
#[inline(always)] |
强制内联 |
#[inline(never)] |
禁止内联 |
#[derive(Debug, Clone, PartialEq)]
struct Point {
x: f64,
y: f64,
}
#[inline(always)]
fn fast_add(a: i32, b: i32) -> i32 {
a + b
}
3.3、 lint 控制类
| 属性 |
作用 |
#[allow(...)] |
允许(忽略)某些 lint 警告 |
#[deny(...)] |
将某些 lint 升级为编译错误 |
#[forbid(...)] |
禁止某些 lint,且无法被覆盖 |
#[warn(...)] |
对某些 lint 发出警告 |
// 忽略未使用代码的警告
#[allow(dead_code)]
fn helper() {}
// 将 unsafe 代码的警告升级为错误
#![deny(unsafe_code)]
// 禁止未使用变量的警告被覆盖
#![forbid(unused_variables)]
3.4 、文档与注释类
| 属性 |
作用 |
#[doc = "..."] |
为项添加文档说明 |
#[deprecated] |
标记项已废弃,使用时产生警告 |
/// 这是文档注释的等价写法
#[doc = "这是一个加法函数"]
pub fn add(a: i32, b: i32) -> i32 {
a + b
}
#[deprecated(since = "0.2.0", note = "请使用 add 函数")]
pub fn old_add(a: i32, b: i32) -> i32 {
a + b
}
3.5、 模块与 crate 配置类
| 属性 |
作用 |
#![crate_name = "..."] |
指定 crate 名称 |
#![crate_type = "..."] |
指定 crate 类型(lib、bin 等) |
#![feature(...)] |
启用不稳定的 Nightly 特性 |
#![no_std] |
不使用标准库 |
// 通常写在 main.rs 或 lib.rs 顶部
#![crate_name = "my_library"]
#![crate_type = "lib"]
// 嵌入式或内核开发中常见
#![no_std]
3.6 、函数与调用约定类
| 属性 |
作用 |
#[test] |
标记测试函数 |
#[ignore] |
跳过该测试 |
#[should_panic] |
期望该测试发生 panic |
#[bench] |
标记基准测试函数(Nightly) |
#[test]
fn test_add() {
assert_eq!(add(1, 2), 3);
}
#[test]
#[should_panic(expected = "除数为零")]
fn test_divide_by_zero() {
let _ = 1 / 0;
}
#[test]
#[ignore = "需要外部数据库"]
fn test_db_connection() {}
3.7、 外部接口与 ABI 类
| 属性 |
作用 |
#[no_mangle] |
关闭符号名改写,供外部链接 |
#[export_name = "..."] |
指定导出符号名 |
#[link(name = "...")] |
链接外部库 |
#[repr(...)] |
指定结构体或枚举的内存布局 |
// FFI 中导出 C 接口
#[no_mangle]
pub extern "C" fn rust_add(a: i32, b: i32) -> i32 {
a + b
}
// 指定内存布局为 C 风格
#[repr(C)]
struct Point {
x: f64,
y: f64,
}
3.8 、异步与运行时类
| 属性 |
作用 |
#[tokio::main] |
将异步函数包装为 tokio 运行时入口 |
#[async_std::main] |
使用 async-std 运行时 |
#[tokio::test] |
标记异步测试函数 |
#[tokio::main]
async fn main() {
println!("Hello from tokio!");
}
#[tokio::test]
async fn test_async_fn() {
let result = async_add(1, 2).await;
assert_eq!(result, 3);
}
4、常用组合实战
4.1 、序列化场景
use serde::{Serialize, Deserialize};
#[derive(Debug, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
struct User {
user_id: u32,
user_name: String,
}
4.2、 条件编译 + 派生组合
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
struct Message {
content: String,
}
4.3、 测试模块完整示例
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_normal() {
assert_eq!(add(2, 3), 5);
}
#[test]
#[should_panic]
fn test_panic() {
panic!("expected");
}
}
5、 常见问题与注意事项
5.1、 外部属性与内部属性的区别
#[...] 作用于下一个项,写在项的上方。
#![...] 作用于整个容器,通常写在文件或模块的开头。
// 错误:内部属性不能写在函数内部中间位置
fn foo() {
// #![allow(dead_code)] // 这样写是错的
}
// 正确:内部属性写在模块开头
mod inner {
#![allow(dead_code)]
fn unused() {}
}
5.2、 属性参数写法
- 布尔型参数直接写属性名即可,如
#[inline]。
- 带值的参数用
key = "value" 或 key = value 形式。
- 多个参数用逗号分隔。
5.3 、自定义属性
Rust 允许通过 #[proc_macro_attribute] 定义自己的属性宏,用于代码生成或逻辑注入:
// 定义自定义属性宏(需要放在单独的 proc-macro crate 中)
#[proc_macro_attribute]
pub fn log_call(_attr: TokenStream, item: TokenStream) -> TokenStream {
// 处理逻辑
item
}
6、总结
Rust 的 Attribute 系统是元编程的重要组成部分,掌握它可以显著提升代码质量与开发效率。核心要点如下:
- 条件编译 用
#[cfg] 实现平台差异化代码。
- 代码生成 用
#[derive] 自动实现 trait。
- lint 控制 用
#[allow]、#[deny] 管理警告。
- 文档与废弃标记 用
#[doc]、#[deprecated] 维护 API 质量。
- 测试标记 用
#[test]、#[should_panic] 组织测试。
- FFI 与内存布局 用
#[no_mangle]、#[repr] 对接外部接口。
建议在项目中逐步实践这些属性,结合 cargo clippy 的提示,让代码更加健壮和规范。
二、代码示例
// ====================== Crate 全局属性 # ======================
#![warn(missing_docs)] // 警告:缺少文档注释
#![allow(unused_imports)] // 允许未使用的use导入
#![cfg_attr(test, allow(unused_variables))] // test模式下关闭未使用变量告警
//! Rust Attribute 综合演示示例(新版安全属性语法)
//!
//! 适配 Rust 1.82+ / Edition 2024,unsafe(no_mangle)
/// 示例模块
pub mod demo {
// -------------------------- 1. repr 内存布局控制(FFI/底层) --------------------------
/// C语言布局枚举,底层存储为u16
#[repr(u16)]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum DeviceCmd {
Read = 0x0001,
Write = 0x0002,
}
/// transparent:单成员包装类型,内存等价于内部u32,零成本FFI封装
#[repr(transparent)]
#[derive(Debug, Clone, Copy)]
pub struct DevHandle(pub u32);
/// packed紧凑对齐 + C布局(谨慎使用,非对齐访问不安全)
#[repr(C, packed(2))]
#[derive(Debug)]
pub struct RawPacket {
pub cmd: u16,
pub len: u8,
}
// -------------------------- 2. non_exhaustive 非穷尽(库扩展兼容) --------------------------
#[non_exhaustive]
#[derive(Debug)]
pub struct Config {
pub timeout: u32,
}
#[non_exhaustive]
pub enum Status {
Ok,
}
// -------------------------- 3. must_use 强制使用返回值 --------------------------
#[must_use = "操作返回的状态必须判断处理,不能忽略"]
pub fn do_io() -> bool {
true
}
// -------------------------- 4. inline / cold 性能优化标记 --------------------------
/// 高频小函数:建议内联
#[inline]
pub fn quick_add(a: i32, b: i32) -> i32 {
a + b
}
/// 强制禁止内联
#[inline(never)]
pub fn heavy_work() {}
/// cold:极少执行的错误冷分支,优化器放至冷代码段
#[cold]
pub fn fatal_error() {
panic!("hard error");
}
// -------------------------- 5. track_caller 获取调用位置 --------------------------
#[track_caller]
pub fn my_assert(cond: bool) {
if !cond {
panic!("断言失败");
}
}
// -------------------------- 6. no_mangle / used FFI 导出C符号【修复unsafe语法】 --------------------------
// SAFETY:全局符号名 c_api_add 保证全局唯一,不会发生符号冲突
#[unsafe(no_mangle)]
pub extern "C" fn c_api_add(a: i32, b: i32) -> i32 {
a + b
}
#[used] // used 不需要unsafe包裹
pub static MAGIC_SIGN: u32 = 0xA5A5_5A5A;
// -------------------------- 7. cfg / cfg_attr 条件编译 --------------------------
#[cfg(windows)]
pub fn platform_print() {
println!("run on windows");
}
#[cfg(not(windows))]
pub fn platform_print() {
println!("run on non‑windows");
}
// 条件附加属性:只有feature="log"时开启must_use
#[cfg_attr(feature = "log", must_use)]
pub fn get_value() -> u32 {
123
}
// -------------------------- 8. doc 文档控制 --------------------------
/// 对外公开文档的函数
pub fn public_func() {}
#[doc(hidden)] // 不生成在rustdoc公开文档里
pub fn internal_helper() {}
// -------------------------- 9. 测试相关属性 --------------------------
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_basic() {
assert_eq!(quick_add(1, 2), 3);
my_assert(true);
}
#[test]
#[should_panic(expected = "hard error")]
fn test_panic() {
fatal_error();
}
#[test]
#[ignore = "这个用例暂时跳过执行"]
fn test_skip() {}
}
}
fn main() {
use demo::*;
let _res = do_io();
let h = DevHandle(0x1000);
println!("{:?}", h);
platform_print();
}