Rust Attribute(属性标记)完整整理

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 以 # 开头,分为两种形式:

  • 外部属性#[...],作用于其后的下一个项(函数、结构体、模块等)。
  • 内部属性#![...],作用于其所在的整个容器(如整个文件、整个模块或整个函数)。
rust 复制代码
// 外部属性:作用于下面的函数
#[allow(dead_code)]
fn unused_fn() {}

// 内部属性:作用于整个文件(通常写在文件开头)
#![allow(dead_code)]

2、Attribute 的语法结构

一个完整的 Attribute 由以下几部分组成:

text 复制代码
# [ 路径 ( 参数 ) ]
  • 路径 :属性名,如 derivecfgallow
  • 参数:可选,可以是名称、值、字符串或嵌套的属性列表。

常见写法示例:

rust 复制代码
// 无参数
#[inline]

// 带一个名称参数
#[cfg(test)]

// 带一个字符串参数
#[doc = "这是文档"]

// 带多个参数
#[allow(dead_code, unused_variables)]

// 带键值对参数
#[deprecated(since = "1.10.0", note = "请使用新函数")]

3、按用途分类整理

3.1、 条件编译类

属性 作用
#[cfg(...)] 按条件编译代码,常用于区分操作系统、架构或特性开关
#[cfg_attr(...)] 当条件满足时再应用另一个属性
rust 复制代码
// 仅在 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(如 DebugClonePartialEq
#[inline] 建议编译器内联该函数
#[inline(always)] 强制内联
#[inline(never)] 禁止内联
rust 复制代码
#[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 发出警告
rust 复制代码
// 忽略未使用代码的警告
#[allow(dead_code)]
fn helper() {}

// 将 unsafe 代码的警告升级为错误
#![deny(unsafe_code)]

// 禁止未使用变量的警告被覆盖
#![forbid(unused_variables)]

3.4 、文档与注释类

属性 作用
#[doc = "..."] 为项添加文档说明
#[deprecated] 标记项已废弃,使用时产生警告
rust 复制代码
/// 这是文档注释的等价写法
#[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] 不使用标准库
rust 复制代码
// 通常写在 main.rs 或 lib.rs 顶部
#![crate_name = "my_library"]
#![crate_type = "lib"]

// 嵌入式或内核开发中常见
#![no_std]

3.6 、函数与调用约定类

属性 作用
#[test] 标记测试函数
#[ignore] 跳过该测试
#[should_panic] 期望该测试发生 panic
#[bench] 标记基准测试函数(Nightly)
rust 复制代码
#[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(...)] 指定结构体或枚举的内存布局
rust 复制代码
// 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] 标记异步测试函数
rust 复制代码
#[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 、序列化场景

rust 复制代码
use serde::{Serialize, Deserialize};

#[derive(Debug, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
struct User {
    user_id: u32,
    user_name: String,
}

4.2、 条件编译 + 派生组合

rust 复制代码
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
struct Message {
    content: String,
}

4.3、 测试模块完整示例

rust 复制代码
#[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、 外部属性与内部属性的区别

  • #[...] 作用于下一个项,写在项的上方。
  • #![...] 作用于整个容器,通常写在文件或模块的开头。
rust 复制代码
// 错误:内部属性不能写在函数内部中间位置
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] 定义自己的属性宏,用于代码生成或逻辑注入:

rust 复制代码
// 定义自定义属性宏(需要放在单独的 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 的提示,让代码更加健壮和规范。

二、代码示例

rust 复制代码
// ====================== Crate 全局属性 #![xxx](作用整个包) ======================
#![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();
}
相关推荐
tedcloud1231 小时前
God‘s Eye View 怎么搭建?在云服务器上部署一个实时 3D 地球可视化平台
linux·服务器·开发语言·后端·rust
深入云栈2 小时前
Netty 4.2.x 源码深度解析 (十三):Epoll 传输 —— Linux 高性能 IO 的 Netty 实现
java·后端
Lyra_Infra2 小时前
从一段错误 JSON 说起:Policy、Role 与 IAM
后端·json·aigc
Charlie_Byte2 小时前
在用户目录安装多版本 JDK,并用环境变量切换
后端
Tairitsu_H2 小时前
[C++] C++11 Lambda与包装器深度解析
开发语言·c++·c++11·lambda·包装器
傲世仙尊2 小时前
从磁盘硬件到Ext文件系统-Linux磁盘级文件系统学习笔记
linux·运维·服务器·开发语言·c++
小羊没烦恼!2 小时前
Memory 记忆设计讨论:为什么 Agent Memory 不能只靠向量数据库?
java·开发语言·windows·算法·c#
黑马程序员毕设2 小时前
基于微信小程序的节目活动报名与管理系统设计与实现
java·开发语言·spring boot·后端·电脑
掘金者阿豪2 小时前
Windows 部署 Papra:建立私有文档库、标签管理,再配置固定公网访问
后端