Rust - 常见属性介绍

文章目录[x]
  1. 1:什么是属性?
  2. 2:条件编译属性
  3. 2.1:#[cfg] 和 #[cfg_attr] - 条件编译详解
  4. 3:代码生成属性
  5. 3.1:#[derive] - 自动实现 Trait 详解
  6. 4:测试相关属性
  7. 4.1:#[test] - 单元测试详解
  8. 5:代码警告控制
  9. 5.1:#[allow], #[warn], #[deny], #[forbid] - Lint 控制详解
  10. 6:文档属性
  11. 6.1:#[doc]
  12. 7:内联和优化属性
  13. 7.1:#[inline] - 函数内联详解
  14. 8:功能开关属性
  15. 8.1:#[feature]
  16. 9:模块和可见性属性
  17. 9.1:#[path]
  18. 10:宏相关属性
  19. 10.1:#[macro_use] 和 #[macro_export]
  20. 11:内存布局属性
  21. 11.1:#[repr] - 内存表示详解
  22. 12:弃用属性
  23. 12.1:#[deprecated]
  24. 13:标准库和外部 crate 常用属性
  25. 13.1:Serde(序列化)
  26. 13.2:异步相关
  27. 14:条件编译实用示例
  28. 15:最佳实践
  29. 16:参考资源

什么是属性?

属性是 Rust 中用于为代码添加元数据的机制。它们以 #[...]#![...] 的形式出现,可以应用于模块、函数、结构体等各种代码项。

  • #[attribute] - 外部属性,应用于紧随其后的项
  • #![attribute] - 内部属性,应用于包含它的项(通常是模块或 crate)

条件编译属性

#[cfg] 和 #[cfg_attr] - 条件编译详解

什么是条件编译?

条件编译允许你根据不同的编译配置(平台、特性、构建模式等)选择性地包含或排除代码。这些代码在不满足条件时完全不会被编译,而不是在运行时检查。

#[cfg] - 条件包含

// 基本语法
#[cfg(条件)]
fn conditional_function() {
    // 仅在条件满足时编译
}

// 实际例子:平台特定代码
#[cfg(target_os = "linux")]
fn get_config_path() -> &'static str {
    "/etc/myapp/config.toml"
}

#[cfg(target_os = "windows")]
fn get_config_path() -> &'static str {
    "C:\\ProgramData\\MyApp\\config.toml"
}

#[cfg(target_os = "macos")]
fn get_config_path() -> &'static str {
    "/Library/Application Support/MyApp/config.toml"
}

常见的配置谓词

1. 操作系统检测

#[cfg(target_os = "linux")]      // Linux
#[cfg(target_os = "windows")]    // Windows
#[cfg(target_os = "macos")]      // macOS
#[cfg(target_os = "ios")]        // iOS
#[cfg(target_os = "android")]    // Android
#[cfg(target_os = "freebsd")]    // FreeBSD
#[cfg(unix)]                     // 所有 Unix-like 系统
#[cfg(windows)]                  // Windows 系统

2. 架构检测

#[cfg(target_arch = "x86_64")]   // 64位 x86
#[cfg(target_arch = "x86")]      // 32位 x86
#[cfg(target_arch = "arm")]      // ARM
#[cfg(target_arch = "aarch64")]  // ARM64
#[cfg(target_arch = "wasm32")]   // WebAssembly
#[cfg(target_pointer_width = "64")]  // 64位指针

3. 构建模式

#[cfg(debug_assertions)]  // Debug 模式(cargo build)
#[cfg(not(debug_assertions))]  // Release 模式(cargo build --release)

// 实际应用
fn expensive_check(data: &[u8]) {
    #[cfg(debug_assertions)]
    {
        // 仅在 Debug 模式下进行昂贵的验证
        assert!(data.len() < 1000, "Data too large");
        for byte in data {
            assert!(*byte < 128, "Invalid byte");
        }
    }

    // Release 模式下这些检查完全不存在
}

4. 测试环境

#[cfg(test)]
mod tests {
    // 仅在运行测试时编译
    use super::*;

    #[test]
    fn test_something() {
        assert_eq!(2 + 2, 4);
    }
}

// 测试辅助函数
#[cfg(test)]
fn create_test_data() -> Vec<u8> {
    vec![1, 2, 3, 4, 5]
}

5. 特性标志(Features)

// Cargo.toml:
// [features]
// encryption = ["aes", "rsa"]
// networking = ["tokio"]

#[cfg(feature = "encryption")]
mod crypto {
    pub fn encrypt(data: &[u8]) -> Vec<u8> {
        // 加密实现
    }
}

#[cfg(feature = "networking")]
use tokio::net::TcpStream;

// 组合特性
#[cfg(all(feature = "encryption", feature = "networking"))]
fn secure_network_send() {
    // 需要同时启用两个特性
}

逻辑组合

// all() - 所有条件都必须满足(AND)
#[cfg(all(unix, target_pointer_width = "64"))]
fn unix_64bit_only() {
    // 仅在 64 位 Unix 系统上编译
}

// any() - 任一条件满足即可(OR)
#[cfg(any(target_os = "linux", target_os = "macos"))]
fn linux_or_mac() {
    println!("Running on Linux or macOS");
}

// not() - 条件不满足时(NOT)
#[cfg(not(target_env = "msvc"))]
fn non_msvc_compiler() {
    // 不使用 MSVC 编译器时
}

// 复杂组合
#[cfg(all(
    unix,
    not(target_os = "macos"),
    any(target_arch = "x86_64", target_arch = "aarch64")
))]
fn complex_condition() {
    // Unix 系统 AND 非 macOS AND (x86_64 OR aarch64)
}

#[cfg_attr] - 条件属性

根据条件动态应用其他属性。

// 语法:#[cfg_attr(条件, 属性)]

// 仅在测试时派生 Debug
#[cfg_attr(test, derive(Debug))]
struct MyStruct {
    data: Vec<u8>,
}

// 多个属性
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(test, derive(PartialEq, Clone))]
pub struct Config {
    pub name: String,
    pub value: i32,
}

// 条件文档
#[cfg_attr(feature = "advanced", doc = "高级功能已启用")]
#[cfg_attr(not(feature = "advanced"), doc = "基础功能")]
pub fn process() {}

实际应用场景

1. 平台特定实现

pub struct FileManager;

impl FileManager {
    #[cfg(unix)]
    pub fn create_lock_file(&self, path: &str) -> std::io::Result<()> {
        use std::os::unix::fs::PermissionsExt;
        let file = std::fs::File::create(path)?;
        let mut perms = file.metadata()?.permissions();
        perms.set_mode(0o600);  // Unix 特有的权限设置
        std::fs::set_permissions(path, perms)?;
        Ok(())
    }

    #[cfg(windows)]
    pub fn create_lock_file(&self, path: &str) -> std::io::Result<()> {
        // Windows 实现
        std::fs::File::create(path)?;
        Ok(())
    }
}

2. 可选依赖

// Cargo.toml:
// [dependencies]
// serde = { version = "1.0", optional = true }

#[cfg(feature = "serde")]
use serde::{Serialize, Deserialize};

#[derive(Clone)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct Data {
    pub value: i32,
}

impl Data {
    #[cfg(feature = "serde")]
    pub fn to_json(&self) -> String {
        serde_json::to_string(self).unwrap()
    }

    #[cfg(not(feature = "serde"))]
    pub fn to_json(&self) -> String {
        panic!("Serde feature not enabled");
    }
}

3. 调试代码

struct Database {
    #[cfg(debug_assertions)]
    query_log: Vec<String>,
}

impl Database {
    fn new() -> Self {
        Database {
            #[cfg(debug_assertions)]
            query_log: Vec::new(),
        }
    }

    fn execute(&mut self, query: &str) {
        #[cfg(debug_assertions)]
        {
            self.query_log.push(query.to_string());
            println!("Executing: {}", query);
        }

        // 实际执行逻辑
    }

    #[cfg(debug_assertions)]
    fn print_log(&self) {
        for (i, query) in self.query_log.iter().enumerate() {
            println!("{}: {}", i, query);
        }
    }
}

4. 性能优化的不同实现

// 在不同平台使用不同的优化实现
pub fn calculate_hash(data: &[u8]) -> u64 {
    #[cfg(all(target_arch = "x86_64", target_feature = "sse4.2"))]
    {
        // 使用 SSE4.2 指令集的快速实现
        unsafe { hash_sse42(data) }
    }

    #[cfg(not(all(target_arch = "x86_64", target_feature = "sse4.2")))]
    {
        // 通用的慢速实现
        hash_generic(data)
    }
}

cfg! 宏 - 运行时检查

#[cfg] 不同,cfg! 在运行时返回布尔值:

fn main() {
    // 编译时所有分支都存在,运行时选择
    if cfg!(target_os = "windows") {
        println!("Running on Windows");
    } else if cfg!(target_os = "linux") {
        println!("Running on Linux");
    }

    // 但是编译器优化后,未使用的分支会被删除
}

// 对比:#[cfg] 编译时就删除了代码
#[cfg(target_os = "windows")]
fn windows_only() {
    // Linux 下编译时此函数根本不存在
}

最佳实践

  1. 优先使用 #[cfg] 而不是运行时 if 检查
  2. 为所有平台提供实现,避免某些平台编译失败
  3. 测试多个配置cargo test --all-features
  4. 文档化特性:说明哪些特性是可选的
  5. 避免过度条件化:太多 #[cfg] 会使代码难以维护

常见陷阱

// 错误:可能在某些平台上缺少函数
#[cfg(target_os = "linux")]
pub fn get_user_name() -> String {
    // 仅 Linux 实现
}

// 正确:所有平台都有实现
#[cfg(target_os = "linux")]
pub fn get_user_name() -> String {
    // Linux 实现
}

#[cfg(not(target_os = "linux"))]
pub fn get_user_name() -> String {
    // 其他平台实现
}

代码生成属性

#[derive] - 自动实现 Trait 详解

什么是 Derive?

#[derive] 允许编译器为你的类型自动生成 trait 的实现代码,避免手写重复的样板代码。这是 Rust 最常用的属性之一。

// 手动实现 Debug
struct Point {
    x: i32,
    y: i32,
}

impl std::fmt::Debug for Point {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        f.debug_struct("Point")
            .field("x", &self.x)
            .field("y", &self.y)
            .finish()
    }
}

// 使用 derive 自动生成相同的代码
#[derive(Debug)]
struct Point {
    x: i32,
    y: i32,
}

标准库可派生的 Trait

1. Debug - 格式化调试输出

#[derive(Debug)]
struct User {
    name: String,
    age: u32,
}

fn main() {
    let user = User { name: "Alice".to_string(), age: 30 };
    println!("{:?}", user);  // User { name: "Alice", age: 30 }
    println!("{:#?}", user); // 美化输出
}

// 要求:所有字段都必须实现 Debug

2. Clone - 深拷贝

#[derive(Clone)]
struct Data {
    values: Vec<i32>,
}

fn main() {
    let data1 = Data { values: vec![1, 2, 3] };
    let data2 = data1.clone();  // 完整复制,包括 Vec 的堆数据
}

// 要求:所有字段都必须实现 Clone
// 性能:可能涉及堆分配,较慢

3. Copy - 按位复制

#[derive(Copy, Clone)]  // Copy 需要 Clone
struct Point {
    x: i32,
    y: i32,
}

fn main() {
    let p1 = Point { x: 1, y: 2 };
    let p2 = p1;  // 按位复制,p1 仍然有效
    println!("{}, {}", p1.x, p2.x);  // 都可以使用
}

// 要求:
// 1. 所有字段都必须实现 Copy
// 2. 不能包含 String, Vec 等拥有堆数据的类型
// 3. 必须同时 derive Clone

4. PartialEqEq - 相等性比较

#[derive(PartialEq)]
struct Point {
    x: i32,
    y: i32,
}

fn main() {
    let p1 = Point { x: 1, y: 2 };
    let p2 = Point { x: 1, y: 2 };
    assert_eq!(p1, p2);  // 逐字段比较
}

// PartialEq: 允许部分相等(如浮点数 NaN != NaN)
#[derive(PartialEq)]
struct FloatPoint {
    x: f64,
    y: f64,
}

// Eq: 完全相等(自反性、对称性、传递性)
#[derive(PartialEq, Eq)]
struct IntPoint {
    x: i32,
    y: i32,
}

// 规则:Eq 需要 PartialEq,且不能包含浮点数

5. PartialOrdOrd - 排序比较

#[derive(PartialEq, PartialOrd)]
struct Person {
    age: u32,
    name: String,
}

fn main() {
    let p1 = Person { age: 30, name: "Alice".to_string() };
    let p2 = Person { age: 25, name: "Bob".to_string() };

    // 按字段顺序比较:先 age,后 name
    assert!(p1 > p2);  // 30 > 25
}

// Ord: 完全排序
#[derive(PartialEq, Eq, PartialOrd, Ord)]
struct Priority {
    level: u32,
}

// 规则:
// - PartialOrd 需要 PartialEq
// - Ord 需要 Eq + PartialOrd
// - 比较顺序:按字段定义顺序

6. Hash - 哈希值计算

use std::collections::HashMap;

#[derive(Hash, PartialEq, Eq)]
struct Key {
    id: u64,
    category: String,
}

fn main() {
    let mut map = HashMap::new();
    let key = Key { id: 1, category: "user".to_string() };
    map.insert(key, "value");
}

// 要求:通常需要同时实现 PartialEq 和 Eq
// 规则:相等的值必须有相同的哈希值

7. Default - 默认值构造

#[derive(Default)]
struct Config {
    timeout: u32,      // 0
    enabled: bool,     // false
    name: String,      // ""
}

fn main() {
    let config = Config::default();
    assert_eq!(config.timeout, 0);
}

// 自定义某些字段的默认值
#[derive(Default)]
struct Settings {
    #[default]  // 需要 nightly 或使用 Default trait
    port: u16,  // 默认 0

    // 或者手动实现
}

// 手动指定默认值
struct CustomConfig {
    timeout: u32,
}

impl Default for CustomConfig {
    fn default() -> Self {
        CustomConfig { timeout: 30 }
    }
}

多个 Trait 组合

// 常见组合
#[derive(Debug, Clone, PartialEq)]
struct Basic {
    value: i32,
}

// 适合作为 HashMap 键
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
struct HashKey {
    id: u64,
}

// 适合排序
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
struct Sortable {
    priority: u32,
}

// 简单数据类型全套
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
struct SimpleData {
    value: i32,
}

派生条件和限制

// 可以派生 Copy(所有字段都是 Copy)
#[derive(Copy, Clone)]
struct Point {
    x: i32,
    y: i32,
}

// 不能派生 Copy(Vec 不是 Copy)
// #[derive(Copy, Clone)]
struct Data {
    values: Vec<i32>,  // 错误!
}

// 可以派生 Clone
#[derive(Clone)]
struct Data {
    values: Vec<i32>,
}

// 不能派生 Eq(包含浮点数)
// #[derive(Eq)]
struct FloatData {
    value: f64,  // 错误!f64 不实现 Eq
}

自定义派生(通过过程宏)

// 使用第三方库的 derive
use serde::{Serialize, Deserialize};

#[derive(Serialize, Deserialize)]
struct User {
    name: String,
    age: u32,
}

// 自定义派生行为
#[derive(Serialize, Deserialize)]
struct Custom {
    #[serde(rename = "userName")]
    name: String,

    #[serde(skip)]
    internal_id: u64,

    #[serde(default)]
    enabled: bool,
}

派生宏的工作原理

// derive 实际生成的代码(简化版)
#[derive(Debug)]
struct Point { x: i32, y: i32 }

// 展开后大致等价于:
struct Point { x: i32, y: i32 }

impl std::fmt::Debug for Point {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        f.debug_struct("Point")
            .field("x", &self.x)
            .field("y", &self.y)
            .finish()
    }
}

控制派生行为

// 忽略某些字段
#[derive(Debug)]
struct User {
    name: String,

    #[debug(skip)]  // 某些库支持
    password: String,
}

// 自定义比较顺序
#[derive(PartialOrd, Ord, PartialEq, Eq)]
struct Task {
    priority: u32,  // 先比较优先级
    name: String,   // 再比较名称
}

// 如果需要不同顺序,手动实现
impl Ord for Task {
    fn cmp(&self, other: &Self) -> std::cmp::Ordering {
        // 自定义比较逻辑
        self.name.cmp(&other.name)
    }
}

性能考虑

// Clone 性能
#[derive(Clone)]
struct Large {
    data: Vec<u8>,      // 需要分配堆内存
    buffer: [u8; 1024], // 复制大块栈内存
}

// 考虑使用 Rc/Arc 避免深拷贝
use std::rc::Rc;
#[derive(Clone)]
struct Shared {
    data: Rc<Vec<u8>>,  // 只复制引用计数
}

// Hash 性能
#[derive(Hash)]
struct Key {
    // 前面的字段更重要,差异性大的字段放前面
    id: u64,
    category: String,
}

最佳实践

  1. 按需派生:不要盲目派生所有 trait
  2. Debug 几乎总是有用的:便于调试
  3. Copy vs Clone:小型栈数据优先 Copy
  4. HashMap 键:需要 Hash + Eq
  5. 排序:需要 OrdPartialOrd
  6. 公共 API:考虑派生更多 trait 提高可用性

常见错误

// 错误 1: Copy 需要 Clone
// #[derive(Copy)]  // 错误
#[derive(Copy, Clone)]  // 正确
struct Point { x: i32 }

// 错误 2: Eq 需要 PartialEq
// #[derive(Eq)]  // 错误
#[derive(PartialEq, Eq)]  // 正确
struct Data { value: i32 }

// 错误 3: Ord 需要 PartialOrd + Eq + PartialEq
#[derive(PartialEq, Eq, PartialOrd, Ord)]  // 正确顺序
struct Ordered { value: i32 }

测试相关属性

#[test] - 单元测试详解

基本用法

#[test] 标记函数为测试函数,由 cargo test 运行。

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

#[test]
fn test_subtraction() {
    assert_eq!(5 - 3, 2);
}

// 测试函数通常返回 () 或 Result<(), E>
#[test]
fn test_with_result() -> Result<(), String> {
    if 2 + 2 == 4 {
        Ok(())
    } else {
        Err(String::from("Math is broken!"))
    }
}

#[should_panic] - 预期 Panic

测试代码应该 panic 的情况。

#[test]
#[should_panic]
fn test_divide_by_zero() {
    let _result = 1 / 0;  // 应该 panic
}

// 指定预期的 panic 消息
#[test]
#[should_panic(expected = "index out of bounds")]
fn test_out_of_bounds() {
    let v = vec![1, 2, 3];
    v[99];  // panic 消息必须包含 "index out of bounds"
}

// 部分匹配
#[test]
#[should_panic(expected = "overflow")]
fn test_overflow() {
    let x: u8 = 255;
    let _y = x + 1;  // debug 模式下会 panic
}

#[ignore] - 忽略测试

标记测试默认不运行(例如耗时测试、外部依赖测试)。

#[test]
#[ignore]
fn expensive_test() {
    // 这个测试很慢,默认跳过
    std::thread::sleep(std::time::Duration::from_secs(10));
}

#[test]
#[ignore = "需要网络连接"]
fn test_network() {
    // 需要外部资源
}

// 运行被忽略的测试:
// cargo test -- --ignored

// 运行所有测试(包括被忽略的):
// cargo test -- --include-ignored

组合使用

#[test]
#[ignore]
#[should_panic]
fn ignored_panic_test() {
    // 被忽略的、预期会 panic 的测试
    panic!("This is fine");
}

// 条件测试
#[test]
#[cfg(target_os = "linux")]
fn linux_only_test() {
    // 仅在 Linux 上运行
}

#[test]
#[cfg(not(target_env = "msvc"))]
fn non_msvc_test() {
    // 非 MSVC 编译器时运行
}

测试组织

// src/lib.rs
pub fn add(a: i32, b: i32) -> i32 {
    a + b
}

pub fn multiply(a: i32, b: i32) -> i32 {
    a * b
}

#[cfg(test)]
mod tests {
    use super::*;

    // 分组测试
    mod addition_tests {
        use super::*;

        #[test]
        fn test_positive() {
            assert_eq!(add(2, 3), 5);
        }

        #[test]
        fn test_negative() {
            assert_eq!(add(-2, -3), -5);
        }
    }

    mod multiplication_tests {
        use super::*;

        #[test]
        fn test_basic() {
            assert_eq!(multiply(3, 4), 12);
        }
    }
}

#[cfg(test)] - 测试专用代码

仅在运行测试时编译的代码。

// 主代码
pub struct Database {
    connection: String,
}

impl Database {
    pub fn new(conn: String) -> Self {
        Database { connection: conn }
    }

    pub fn query(&self, sql: &str) -> Vec<String> {
        // 实际查询逻辑
        vec![]
    }
}

// 仅测试时存在的代码
#[cfg(test)]
impl Database {
    // 测试专用的构造函数
    pub fn new_test() -> Self {
        Database {
            connection: "test://localhost".to_string(),
        }
    }

    // 测试辅助方法
    pub fn insert_test_data(&mut self) {
        // 插入测试数据
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_database() {
        let mut db = Database::new_test();  // 仅在测试中可用
        db.insert_test_data();
        let results = db.query("SELECT * FROM users");
        assert!(!results.is_empty());
    }
}

// 测试辅助函数
#[cfg(test)]
fn create_test_user() -> User {
    User {
        name: "Test User".to_string(),
        age: 30,
    }
}

断言宏详解

#[test]
fn test_assertions() {
    // assert! - 条件为真
    assert!(2 + 2 == 4);
    assert!(true, "可选的失败消息");

    // assert_eq! - 相等断言
    assert_eq!(2 + 2, 4);
    assert_eq!(vec![1, 2], vec![1, 2], "向量应该相等");

    // assert_ne! - 不等断言
    assert_ne!(2 + 2, 5);

    // debug_assert! - 仅 Debug 模式检查
    debug_assert!(expensive_check(), "仅在 Debug 模式验证");
}

#[test]
#[should_panic(expected = "assertion failed")]
fn test_failing_assertion() {
    assert_eq!(2 + 2, 5, "数学不对劲");
}

基准测试 #[bench]

仅在 nightly 编译器可用。

#![feature(test)]
extern crate test;

#[bench]
fn bench_addition(b: &mut test::Bencher) {
    b.iter(|| {
        let _result = 2 + 2;
    });
}

#[bench]
fn bench_vector_creation(b: &mut test::Bencher) {
    b.iter(|| {
        let v: Vec<i32> = (0..1000).collect();
        test::black_box(v);  // 防止编译器优化掉
    });
}

// 运行基准测试:
// cargo +nightly bench

集成测试

// tests/integration_test.rs(项目根目录的 tests 文件夹)
use my_crate::*;

#[test]
fn test_public_api() {
    let result = add(2, 3);
    assert_eq!(result, 5);
}

#[test]
#[ignore]
fn test_expensive_integration() {
    // 耗时的集成测试
}

测试最佳实践

// 1. 命名清晰
#[test]
fn test_add_positive_numbers_returns_sum() {
    assert_eq!(add(2, 3), 5);
}

// 2. 一个测试一个概念
#[test]
fn test_add_handles_zero() {
    assert_eq!(add(0, 5), 5);
    assert_eq!(add(5, 0), 5);
}

// 3. 测试边界情况
#[test]
fn test_add_max_values() {
    assert_eq!(add(i32::MAX, 0), i32::MAX);
}

#[test]
#[should_panic]
fn test_add_overflow() {
    let _result = add(i32::MAX, 1);
}

// 4. 使用辅助函数减少重复
#[cfg(test)]
fn setup_test_environment() -> TestContext {
    TestContext::new()
}

#[test]
fn test_with_setup() {
    let ctx = setup_test_environment();
    // 使用 ctx 进行测试
}

// 5. 文档测试
/// 加法函数
///
/// # Examples
///
/// ```
/// use my_crate::add;
/// assert_eq!(add(2, 3), 5);
/// ```
pub fn add(a: i32, b: i32) -> i32 {
    a + b
}

运行测试的选项

# 运行所有测试
cargo test

# 运行特定测试
cargo test test_addition

# 运行特定模块的测试
cargo test tests::addition_tests

# 显示测试输出(println! 等)
cargo test -- --nocapture

# 并行运行测试(指定线程数)
cargo test -- --test-threads=4

# 单线程运行
cargo test -- --test-threads=1

# 运行被忽略的测试
cargo test -- --ignored

# 运行所有测试(包括被忽略的)
cargo test -- --include-ignored

# 仅运行文档测试
cargo test --doc

# 运行集成测试
cargo test --test integration_test

条件测试编译

#[test]
#[cfg(feature = "expensive-tests")]
fn expensive_feature_test() {
    // 仅在启用特性时运行
}

// Cargo.toml:
// [features]
// expensive-tests = []

// 运行:cargo test --features expensive-tests

代码警告控制

#[allow], #[warn], #[deny], #[forbid] - Lint 控制详解

Lint 级别说明

Rust 编译器有四个 lint 级别,从宽松到严格:

// 1. #[allow] - 允许,抑制警告
#[allow(unused_variables)]
fn example() {
    let x = 5;  // 不会警告
}

// 2. #[warn] - 警告,但允许编译
#[warn(non_snake_case)]
fn myFunction() {  // 警告:应该用 my_function
    // 编译通过,但显示警告
}

// 3. #[deny] - 拒绝,视为错误
#[deny(unsafe_code)]
fn dangerous() {
    // unsafe { }  // 编译错误!
}

// 4. #[forbid] - 禁止,且不能被覆盖
#[forbid(unsafe_code)]
mod strictly_safe {
    // #[allow(unsafe_code)]  // 无效!forbid 不能被覆盖
    // fn cant_override() {
    //     unsafe { }  // 仍然是错误
    // }
}

#[allow] vs #[forbid] 的区别

// allow 可以被内部代码覆盖
#[allow(unused_variables)]
mod outer {
    #[warn(unused_variables)]  // 可以重新启用警告
    fn inner() {
        let x = 5;  // 会警告
    }
}

// forbid 不能被覆盖
#[forbid(unused_variables)]
mod strict {
    // #[allow(unused_variables)]  // 编译错误!
    fn inner() {
        let x = 5;  // 错误
    }
}

常见 Lint 类别

1. 代码质量相关

// 未使用的代码
#[allow(dead_code)]
fn unused_function() {
    // 这个函数从未被调用
}

#[allow(unused_variables)]
fn example() {
    let unused_var = 42;
}

#[allow(unused_imports)]
mod m {
    use std::collections::HashMap;  // 未使用
}

#[allow(unused_mut)]
fn foo() {
    let mut x = 5;  // 实际不需要 mut
    println!("{}", x);
}

// 无法访问的代码
#[allow(unreachable_code)]
fn unreachable() {
    return;
    println!("永远不会执行");
}

2. 命名规范相关

// 函数/变量应该用 snake_case
#[allow(non_snake_case)]
fn myFunction() {}  // 应该是 my_function

#[allow(non_snake_case)]
let myVariable = 42;  // 应该是 my_variable

// 类型应该用 CamelCase
#[allow(non_camel_case_types)]
struct my_struct {}  // 应该是 MyStruct

// 常量应该用 SCREAMING_SNAKE_CASE
#[allow(non_upper_case_globals)]
const myConstant: i32 = 42;  // 应该是 MY_CONSTANT

// 完整示例
#[allow(non_snake_case, non_camel_case_types)]
mod legacy_code {
    pub struct oldStruct {
        pub someField: i32,
    }

    pub fn LegacyFunction() {}
}

3. 类型和安全相关

// 不安全代码
#[deny(unsafe_code)]
mod safe_module {
    // unsafe { }  // 错误!
}

// 允许不安全代码(显式声明意图)
#[allow(unsafe_code)]
fn low_level_operation() {
    unsafe {
        // 底层操作
    }
}

// 类型推断失败
#[allow(type_alias_bounds)]
type MyType<T: Trait> = Box<T>;  // 可能无效的 trait 约束

// 裸指针解引用
#[warn(unsafe_op_in_unsafe_fn)]
unsafe fn careful() {
    let ptr = 0x1234 as *const i32;
    let _x = *ptr;  // 警告:不安全操作
}

4. 模式匹配相关

// 未处理所有情况
#[allow(unreachable_patterns)]
fn match_example(x: i32) {
    match x {
        _ => {},
        1 => {},  // 永远不会匹配
    }
}

// 非穷尽的模式
enum Color {
    Red,
    Green,
    Blue,
}

#[allow(non_exhaustive_omitted_patterns)]
fn handle_color(c: Color) {
    match c {
        Color::Red => {},
        // 缺少 Green 和 Blue
        _ => {},
    }
}

5. 文档和注释相关

// 缺少文档注释
#[warn(missing_docs)]
pub mod my_module {
    /// 这个有文档
    pub fn documented() {}

    // #[allow(missing_docs)]
    pub fn undocumented() {}  // 会警告
}

// 全局启用
#![warn(missing_docs)]

// 无效的文档
#[allow(rustdoc::broken_intra_doc_links)]
/// 参见 [`NonExistent`]  // 链接无效
pub fn example() {}

6. 性能和优化相关

// 可能的性能问题
#[allow(clippy::needless_collect)]
fn inefficient() {
    let v: Vec<_> = (0..10).collect();
    let sum: i32 = v.iter().sum();  // 可以直接 sum,不需要 collect
}

// 盒装切片可以简化
#[allow(clippy::box_collection)]
fn boxed() {
    let _b: Box<Vec<i32>> = Box::new(vec![1, 2, 3]);
}

作用范围

// 1. 整个 crate
#![allow(unused_variables)]  // 内部属性(注意感叹号)

// 2. 模块
#[allow(dead_code)]
mod my_module {
    fn unused() {}  // 允许
}

// 3. 单个函数
#[allow(unused_variables)]
fn my_function() {
    let x = 5;  // 允许
}

// 4. 单个代码块
fn example() {
    #[allow(unused_variables)]
    {
        let x = 5;  // 允许
    }
    let y = 10;  // 不允许(如果未使用)
}

// 5. 单个语句
fn statement_level() {
    #[allow(unused_variables)]
    let x = 5;  // 允许

    let y = 10;  // 不允许(如果未使用)
}

组合多个 Lint

// 单个属性多个 lint
#[allow(dead_code, unused_variables, non_snake_case)]
fn complex_example() {
    let UnusedVar = 42;
}

// 多个属性
#[allow(dead_code)]
#[allow(unused_variables)]
fn example() {}

// 不同级别组合
#[warn(unused_variables)]
#[deny(unsafe_code)]
mod my_module {}

Clippy Lints

Clippy 是 Rust 的 lint 工具,提供更多检查。

// 允许特定的 Clippy lint
#[allow(clippy::too_many_arguments)]
fn many_args(a: i32, b: i32, c: i32, d: i32, e: i32, f: i32, g: i32, h: i32) {}

#[allow(clippy::single_match)]
fn single_match(x: Option<i32>) {
    match x {
        Some(_) => {},
        None => {},
    }
}

// 常见 Clippy lints
#[allow(clippy::unnecessary_wraps)]
fn always_ok() -> Result<i32, ()> {
    Ok(42)  // 永远不会失败
}

#[allow(clippy::len_without_is_empty)]
struct MyVec {
    data: Vec<i32>,
}

impl MyVec {
    fn len(&self) -> usize {
        self.data.len()
    }
    // Clippy 建议:有 len() 就应该有 is_empty()
}

全局 Lint 配置

// src/lib.rs 或 src/main.rs 顶部
#![warn(missing_docs)]
#![warn(unused_imports)]
#![deny(unsafe_code)]
#![allow(dead_code)]  // 开发阶段临时允许

// 分组管理
#![warn(
    missing_docs,
    unused_imports,
    unused_variables,
)]

#![deny(
    unsafe_code,
    unstable_features,
)]

实际应用场景

1. 与外部 C 代码交互

#[allow(non_camel_case_types)]
#[allow(non_snake_case)]
#[repr(C)]
struct legacy_c_struct {
    Field1: i32,
    Field2: i32,
}

2. 生成的代码

#[allow(dead_code, unused_variables, non_snake_case)]
mod generated {
    // 自动生成的代码,不需要警告
    include!("generated.rs");
}

3. 开发中的代码

#[allow(dead_code, unused_variables)]
#[cfg(feature = "experimental")]
mod experimental {
    // 实验性功能,还在开发中
    pub fn work_in_progress() {
        let placeholder = 42;
    }
}

4. 向后兼容

#[deprecated(since = "1.2.0", note = "使用 new_function 代替")]
#[allow(dead_code)]
pub fn old_function() {
    // 保留旧 API 用于向后兼容
}

最佳实践

// 好的做法:范围最小化
fn example() {
    #[allow(unused_variables)]
    let temp = expensive_computation();  // 仅这一行允许

    // 其他代码仍然检查
}

// 避免:过度使用
#[allow(warnings)]  // 禁用所有警告,太宽泛
fn bad_example() {}

// 好的做法:添加注释说明原因
#[allow(unsafe_code)]  // 需要直接操作内存以提高性能
unsafe fn optimize() {}

// 好的做法:开发时临时允许,发布前移除
#[cfg(debug_assertions)]
#[allow(dead_code)]
fn debug_helper() {}

查看所有可用的 Lints

# 查看编译器 lints
rustc -W help

# 查看 Clippy lints
cargo clippy -- -W help

# 运行 Clippy
cargo clippy

# Clippy 更严格模式
cargo clippy -- -W clippy::pedantic

Lint 组

// warnings - 所有警告
#![deny(warnings)]  // 将所有警告视为错误

// nonstandard_style - 命名规范
#![warn(nonstandard_style)]

// rust-2018-idioms - Rust 2018 习惯用法
#![warn(rust_2018_idioms)]

// unused - 所有"未使用"类警告
#![warn(unused)]

文档属性

#[doc]

为代码添加文档注释。

/// 这是一个文档注释(等同于 #[doc = "..."])
///
/// # Examples
///
/// ```
/// let result = add(2, 3);
/// assert_eq!(result, 5);
/// ```
pub fn add(a: i32, b: i32) -> i32 {
    a + b
}

#[doc(hidden)]
pub fn internal_function() {
    // 在文档中隐藏
}

内联和优化属性

#[inline] - 函数内联详解

什么是内联?

内联(Inlining) 是编译器的一种优化技术,它将函数调用替换为函数体本身的代码。简单来说,就是把函数的代码直接"复制粘贴"到调用它的地方,而不是通过传统的函数调用机制。

示例对比:

// 未内联的代码
fn add(a: i32, b: i32) -> i32 {
    a + b
}

fn main() {
    let result = add(5, 3);  // 函数调用开销:压栈、跳转、返回
}

// 内联后的等效代码(编译器优化后)
fn main() {
    let result = 5 + 3;  // 直接执行,无函数调用开销
}

内联的优点

  1. 消除函数调用开销
    • 避免参数压栈/出栈
    • 避免跳转指令
    • 避免返回地址保存和恢复
    • 对于小型函数,这些开销可能比函数体本身还大
  2. 启用更多优化机会
    #[inline]
    fn square(x: i32) -> i32 {
       x * x
    }
    
    fn compute() -> i32 {
       square(5)  // 编译器能看到 x = 5,可以编译时计算
    }
    // 内联后,编译器可以直接优化为 25
    
  3. 提高缓存友好性
    • 减少指令跳转,提高 CPU 指令缓存命中率
    • 代码局部性更好
  4. 允许常量折叠和死代码消除
    #[inline]
    fn get_debug_level() -> u32 {
       if cfg!(debug_assertions) { 2 } else { 0 }
    }
    
    fn log(msg: &str) {
       if get_debug_level() > 0 {  // 内联后,release 模式此分支可被完全删除
           println!("{}", msg);
       }
    }
    

内联的缺点

  1. 代码膨胀(Code Bloat)
    #[inline]
    fn large_function() {
       // 100 行代码...
    }
    
    fn caller1() { large_function(); }  // 复制 100 行
    fn caller2() { large_function(); }  // 再复制 100 行
    fn caller3() { large_function(); }  // 又复制 100 行
    // 二进制文件大小显著增加
    
    • 每个调用点都复制一份函数体
    • 增加二进制文件大小
    • 可能超出指令缓存容量,反而降低性能
  2. 编译时间增加
    • 编译器需要在每个调用点进行优化分析
    • 大量内联会显著延长编译时间
  3. 破坏代码局部性
    • 过度内联可能导致热点代码分散
    • 影响 CPU 缓存效率
  4. 调试困难
    • 内联后的代码在调试器中难以追踪
    • 堆栈跟踪可能不准确
  5. 不利于动态库
    • 内联的函数无法在运行时更新
    • 库更新时需要重新编译依赖项

内联变体详解

// 1. #[inline] - 提示编译器考虑内联
#[inline]
pub fn suggested_inline() -> i32 {
    42
}
// 跨 crate 调用时必须标记,否则编译器看不到函数体
// 编译器可以忽略此提示

// 2. #[inline(always)] - 强制内联(除非不可能)
#[inline(always)]
pub fn forced_inline() -> i32 {
    100
}
// 警告:可能导致代码膨胀
// 适用于:性能关键的微小函数

// 3. #[inline(never)] - 禁止内联
#[inline(never)]
pub fn no_inline() -> i32 {
    200
}
// 适用于:调试、性能分析、减少代码膨胀

// 4. 无属性 - 编译器自行决定
pub fn auto_inline() -> i32 {
    300
}
// 同一 crate 内:编译器可能自动内联
// 跨 crate:默认不内联(看不到函数体)

何时使用内联

应该使用 #[inline] 的场景:

  1. 小型函数(1-3 行)
    #[inline]
    pub fn is_empty(&self) -> bool {
       self.len == 0
    }
    
  2. Getter/Setter 方法
    #[inline]
    pub fn get_x(&self) -> i32 {
       self.x
    }
    
  3. 性能关键的热点路径
    #[inline]
    pub fn fast_hash(data: &[u8]) -> u64 {
       // 频繁调用的哈希函数
    }
    
  4. 泛型函数(几乎总是应该内联)
    #[inline]
    pub fn max<T: Ord>(a: T, b: T) -> T {
       if a > b { a } else { b }
    }
    // 内联后可以针对具体类型优化
    
  5. 跨 crate 的公共小函数
    #[inline]
    pub fn add_one(x: i32) -> i32 {
       x + 1
    }
    // 没有 #[inline],外部 crate 无法内联
    

不应该使用的场景:

  1. 大型函数(>10 行或包含循环)
  2. 很少被调用的函数
  3. 递归函数(编译器通常无法内联)
  4. 已经足够快的函数

实际性能案例

use std::time::Instant;

// 案例 1:微小函数 - 内联受益明显
#[inline]
fn add_inline(a: i32, b: i32) -> i32 {
    a + b
}

fn add_no_inline(a: i32, b: i32) -> i32 {
    a + b
}

// 案例 2:中等函数 - 权衡利弊
#[inline(never)]  // 为了对比,禁止内联
fn complex_no_inline(x: i32) -> i32 {
    let mut result = x;
    for i in 0..10 {
        result = result.wrapping_mul(i).wrapping_add(7);
    }
    result
}

#[inline]
fn complex_inline(x: i32) -> i32 {
    let mut result = x;
    for i in 0..10 {
        result = result.wrapping_mul(i).wrapping_add(7);
    }
    result
}

// 基准测试显示:
// - add_inline: 快 2-3 倍(函数调用开销占比大)
// - complex_inline: 快 5-10%(函数体执行时间占主导)

编译器的智能决策

// Rust 编译器在以下情况自动内联(即使没有 #[inline]):
// 1. 同一编译单元内的私有函数
fn internal_helper() -> i32 {
    42
}

// 2. LTO(链接时优化)开启时
// Cargo.toml: lto = true

// 3. 明显有益的情况(编译器能自行判断)

最佳实践建议

  1. 默认不加 #[inline],让编译器决定
  2. 公共 API 的小函数(<5 行)添加 #[inline]
  3. 性能敏感代码先用性能分析工具测量,再决定
  4. 避免 #[inline(always)],除非有明确证据
  5. 泛型函数几乎总是需要 #[inline]

验证内联效果

# 查看汇编代码,确认是否内联
cargo rustc --release -- --emit asm

# 使用 cargo-asm 查看特定函数
cargo install cargo-asm
cargo asm --release my_crate::my_function

# 性能基准测试
cargo bench

功能开关属性

#[feature]

启用不稳定的实验性功能(仅限 nightly 编译器)。

#![feature(const_fn_floating_point_arithmetic)]
#![feature(never_type)]

模块和可见性属性

#[path]

指定模块文件的路径。

#[path = "custom/path/to/module.rs"]
mod my_module;

宏相关属性

#[macro_use] 和 #[macro_export]

// 导入外部 crate 的宏
#[macro_use]
extern crate serde_derive;

// 导出宏供其他 crate 使用
#[macro_export]
macro_rules! my_macro {
    () => { println!("Hello!"); }
}

内存布局属性

#[repr] - 内存表示详解

什么是 repr?

#[repr] 控制类型在内存中的布局方式。Rust 默认不保证结构体的内存布局,这允许编译器优化,但在与 C 代码交互或进行底层编程时,需要精确控制内存布局。

#[repr(Rust)] - 默认布局

// 默认布局(不需要显式标注)
struct DefaultLayout {
    a: u8,   // 1 字节
    b: u32,  // 4 字节
    c: u16,  // 2 字节
}

// Rust 可能重排字段以减少内存占用和提高对齐:
// 实际内存布局可能是:
// b: u32 (偏移 0, 4字节)
// c: u16 (偏移 4, 2字节)
// a: u8  (偏移 6, 1字节)
// + 1 字节填充 = 总共 8 字节

// 字段顺序不影响功能,但影响内存布局

#[repr(C)] - C 语言兼容布局

用于与 C 代码交互,保证字段按声明顺序排列。

#[repr(C)]
struct Point {
    x: i32,  // 偏移 0
    y: i32,  // 偏移 4
}
// 总大小: 8 字节,与 C 的 struct Point 完全兼容

#[repr(C)]
struct MixedSize {
    a: u8,   // 偏移 0 (1 字节)
    // 3 字节填充,对齐到 4 字节边界
    b: u32,  // 偏移 4 (4 字节)
    c: u16,  // 偏移 8 (2 字节)
    // 2 字节填充,对齐到 4 字节边界
}
// 总大小: 12 字节

// FFI 使用示例
#[repr(C)]
struct Config {
    enabled: bool,
    timeout: u32,
    name: [u8; 32],
}

extern "C" {
    fn process_config(config: *const Config);
}

fn main() {
    let cfg = Config {
        enabled: true,
        timeout: 30,
        name: [0; 32],
    };
    unsafe {
        process_config(&cfg);
    }
}

#[repr(C)] 枚举

// C 风格枚举
#[repr(C)]
enum Status {
    Ok = 0,
    Error = 1,
    Pending = 2,
}

// 带数据的枚举(tagged union)
#[repr(C)]
enum Message {
    Quit,
    Move { x: i32, y: i32 },
    Write(String),
}

// 指定判别值类型
#[repr(C, u8)]
enum SmallEnum {
    A,  // 0
    B,  // 1
    C,  // 2
}
// 判别值用 u8 存储,节省空间

#[repr(transparent)] - 透明包装

只有一个非零大小字段的包装类型,内存布局与内部字段完全相同。

#[repr(transparent)]
struct Wrapper(u32);

// 在内存中,Wrapper 和 u32 完全一样
// 可以安全地在 FFI 边界传递

#[repr(transparent)]
struct UserId(u64);

#[repr(transparent)]
struct Meters(f64);

// 用于类型安全,同时保持二进制兼容
extern "C" {
    fn process_id(id: u64);
}

fn safe_process(user: UserId) {
    unsafe {
        // UserId 和 u64 布局完全相同
        process_id(user.0);
    }
}

// 错误:多个非零大小字段
// #[repr(transparent)]
// struct Invalid(u32, u64);  // 编译错误

// 正确:可以有多个零大小字段
#[repr(transparent)]
struct Valid<T>(T, PhantomData<String>);

#[repr(packed)] - 紧凑布局

移除字段之间的填充,减小结构体大小,但可能导致未对齐访问。

#[repr(packed)]
struct Packed {
    a: u8,   // 偏移 0
    b: u32,  // 偏移 1 (未对齐!)
    c: u16,  // 偏移 5
}
// 总大小: 7 字节(无填充)

// 对比:不使用 packed
struct Normal {
    a: u8,   // 偏移 0
    // 3 字节填充
    b: u32,  // 偏移 4
    c: u16,  // 偏移 8
    // 2 字节填充
}
// 总大小: 12 字节

// ⚠️ 警告:访问未对齐字段很危险
#[repr(packed)]
struct Dangerous {
    x: u8,
    y: u64,  // 可能未对齐
}

fn use_packed() {
    let d = Dangerous { x: 1, y: 2 };

    // ❌ 错误:创建未对齐引用是未定义行为
    // let y_ref = &d.y;

    // ✅ 正确:使用 ptr::addr_of!
    let y_value = unsafe {
        std::ptr::addr_of!(d.y).read_unaligned()
    };
}

#[repr(packed(N))] - 指定对齐

// 对齐到 2 字节边界
#[repr(packed(2))]
struct Packed2 {
    a: u8,   // 偏移 0
    // 1 字节填充
    b: u32,  // 偏移 2 (2字节对齐)
    c: u16,  // 偏移 6
}
// 总大小: 8 字节

#[repr(packed(4))]
struct Packed4 {
    a: u8,   // 偏移 0
    // 3 字节填充
    b: u32,  // 偏移 4
    c: u16,  // 偏移 8
}

#[repr(align(N))] - 指定最小对齐

强制类型对齐到指定字节边界。

// 对齐到 16 字节边界(SIMD 优化)
#[repr(align(16))]
struct Aligned {
    data: [u8; 12],
}

// 缓存行对齐(64 字节)
#[repr(align(64))]
struct CacheLine {
    data: [u8; 64],
}

// 组合使用
#[repr(C, align(8))]
struct Combined {
    x: u32,
    y: u32,
}

// 实际应用:避免伪共享
#[repr(align(64))]
struct Counter {
    value: AtomicU64,
    // 填充到 64 字节,确保每个 Counter 独占一个缓存行
}

整数表示 #[repr(u8)], #[repr(i32)]

指定枚举的判别值类型。

// 默认使用 isize
enum Default {
    A, B, C,
}

// 使用 u8(节省空间)
#[repr(u8)]
enum Small {
    A = 0,
    B = 1,
    C = 2,
}

// 使用 u32
#[repr(u32)]
enum Medium {
    Low = 100,
    High = 200,
}

// 实际应用:网络协议
#[repr(u16)]
enum PacketType {
    Data = 0x0001,
    Ack = 0x0002,
    Error = 0x0003,
}

fn serialize(packet_type: PacketType) -> [u8; 2] {
    let value = packet_type as u16;
    value.to_le_bytes()
}

组合不同的 repr

// C 布局 + u8 判别值
#[repr(C, u8)]
enum CEnum {
    A,
    B,
    C,
}

// C 布局 + 对齐
#[repr(C, align(8))]
struct Aligned {
    x: u32,
}

// 错误:不能同时使用 packed 和 align
// #[repr(packed, align(8))]
// struct Invalid {}

// 错误:transparent 只能单独使用
// #[repr(transparent, C)]
// struct Invalid(u32);

实际应用场景

1. FFI (Foreign Function Interface)

// 与 C 库交互
#[repr(C)]
struct FileInfo {
    size: u64,
    modified: u64,
    is_dir: bool,
}

#[link(name = "mylib")]
extern "C" {
    fn get_file_info(path: *const u8) -> FileInfo;
}

2. 网络协议

#[repr(C, packed)]
struct TcpHeader {
    src_port: u16,
    dst_port: u16,
    seq_num: u32,
    ack_num: u32,
    flags: u16,
    window: u16,
    checksum: u16,
    urgent: u16,
}

3. 内存映射 I/O

#[repr(C)]
struct DeviceRegisters {
    control: u32,
    status: u32,
    data: u32,
}

// 映射到固定内存地址
const DEVICE_BASE: usize = 0x4000_0000;
let registers = unsafe {
    &mut *(DEVICE_BASE as *mut DeviceRegisters)
};

4. SIMD 优化

#[repr(align(16))]
struct Vec4 {
    data: [f32; 4],
}

// 确保 16 字节对齐,适用于 SSE 指令

5. 无锁数据结构

// 避免伪共享
#[repr(align(64))]
struct PaddedAtomic {
    value: AtomicU64,
}

struct LockFreeQueue {
    head: PaddedAtomic,  // 独占缓存行
    tail: PaddedAtomic,  // 独占缓存行
}

查看内存布局

use std::mem::{size_of, align_of};

#[repr(C)]
struct Example {
    a: u8,
    b: u32,
    c: u16,
}

fn main() {
    println!("Size: {}", size_of::<Example>());      // 12
    println!("Align: {}", align_of::<Example>());    // 4

    println!("Offset of a: {}", offset_of!(Example, a));  // 需要 memoffset crate
    println!("Offset of b: {}", offset_of!(Example, b));
    println!("Offset of c: {}", offset_of!(Example, c));
}

最佳实践

  1. 默认使用 Rust 布局:让编译器优化
  2. FFI 使用 #[repr(C)]:与 C 代码交互时必需
  3. 谨慎使用 packed:未对齐访问可能导致性能问题或崩溃
  4. transparent 用于零成本抽象:newtype 模式
  5. 性能关键代码考虑 align:SIMD、缓存优化

常见陷阱

// 错误:未对齐访问
#[repr(packed)]
struct Bad {
    x: u8,
    y: u64,
}

fn broken(b: &Bad) {
    let _y = b.y;  // 可能崩溃或产生错误结果
}

// 正确:使用 read_unaligned
fn correct(b: &Bad) {
    let y = unsafe {
        std::ptr::addr_of!(b.y).read_unaligned()
    };
}

// 错误:transparent 要求恰好一个非零大小字段
// #[repr(transparent)]
// struct Invalid(u32, u64);

// 正确
#[repr(transparent)]
struct Valid(u32);

弃用属性

#[deprecated]

标记代码为已弃用。

#[deprecated]
fn old_function() {}

#[deprecated(since = "1.2.0", note = "请使用 new_function 代替")]
fn legacy_function() {
    // ...
}

标准库和外部 crate 常用属性

Serde(序列化)

use serde::{Serialize, Deserialize};

#[derive(Serialize, Deserialize)]
struct User {
    #[serde(rename = "username")]
    name: String,

    #[serde(skip)]
    password: String,

    #[serde(default)]
    active: bool,
}

异步相关

// Tokio
#[tokio::main]
async fn main() {
    // 异步主函数
}

#[tokio::test]
async fn test_async() {
    // 异步测试
}

条件编译实用示例

// 根据特性开关编译
#[cfg(feature = "advanced")]
mod advanced_features;

// 组合条件
#[cfg(all(unix, target_pointer_width = "64"))]
fn unix_64bit_only() {}

#[cfg(any(windows, target_os = "macos"))]
fn windows_or_mac() {}

#[cfg(not(debug_assertions))]
fn release_only() {}

最佳实践

  1. 合理使用 #[inline]:过度使用可能导致代码膨胀
  2. 文档注释:为公共 API 添加详细文档
  3. 条件编译:用于平台特定代码和可选功能
  4. 派生 trait:尽可能使用 #[derive] 减少样板代码
  5. 警告控制:谨慎使用 #[allow],不要隐藏真正的问题

参考资源

点赞

发表评论

昵称和uid可以选填一个,填邮箱必填(留言回复后将会发邮件给你)
tips:输入uid可以快速获得你的昵称和头像

Title - Artist
0:00