Rust基础(常见的标准宏)

Rust中的标准宏

宏名称 功能 说明
concat_bytes 拼接字面量为一个字节切片Experimental Concatenates literals into a byte slice. 实验性(需要nightly版本)
concat_idents 拼接多个标识符为一个Experimental Concatenates identifiers into one identifier. 实验性
const_format_args Experimental Same as format_args, but can be used in some const contexts. 实验性
format_args_nl Experimental Same as format_args, but adds a newline in the end. 实验性
log_syntax Experimental Prints passed tokens into the standard output. 实验性
trace_macros Experimental Enables or disables tracing functionality used for debugging other macros. 实验性
assert Asserts that a boolean expression is true at runtime.
assert_eq Asserts that two expressions are equal to each other (using PartialEq).
assert_ne Asserts that two expressions are not equal to each other (using PartialEq).
cfg Evaluates boolean combinations of configuration flags at compile-time.
column Expands to the column number at which it was invoked.
compile_error Causes compilation to fail with the given error message when encountered.
concat Concatenates literals into a static string slice.
dbg Prints and returns the value of a given expression for quick and dirty debugging.
debug_assert Asserts that a boolean expression is true at runtime.
debug_assert_eq Asserts that two expressions are equal to each other.
debug_assert_ne Asserts that two expressions are not equal to each other.
env Inspects an environment variable at compile time.
eprint 打印标准错误Prints to the standard error.
eprintln 打印标准错误(一行行打印)Prints to the standard error, with a newline.
file 在调用处扩展文件名称Expands to the file name in which it was invoked.
format Creates a String using interpolation of runtime expressions.
format_args Constructs parameters for the other string-formatting macros.
include Parses a file as an expression or an item according to the context.
include_bytes Includes a file as a reference to a byte array.
include_str Includes a UTF-8 encoded file as a string.
is_x86_feature_detected x86 or x86-64 A macro to test at runtime whether a CPU feature is available on x86/x86-64 platforms.
line Expands to the line number on which it was invoked.
matches Returns whether the given expression matches any of the given patterns.
module_path Expands to a string that represents the current module path.
option_env Optionally inspects an environment variable at compile time.
panic Panics the current thread.
print Prints to the standard output.
println Prints to the standard output, with a newline.
stringify Stringifies its arguments.
thread_local Declare a new thread local storage key of type std::thread::LocalKey.
todo Indicates unfinished code.
tryDeprecated Unwraps a result or propagates its error.
unimplemented Indicates unimplemented code by panicking with a message of “not implemented”.
unreachable Indicates unreachable code.
vec Creates a Vec containing the arguments.
write Writes formatted data into a buffer.
writeln Write formatted data into a buffer, with a newline appended.

cancat_bytes示例

宏实现原型:

#![feature(concat_bytes)]
fn main() {
    let s: &[u8; 6] = concat_bytes!(b'A', b"BC", [68, b'E', 70]);
    assert_eq!(s, b"ABCDEF")
}

rustc 1.72.0 (5680fa18f 2023-08-23) 不可用

assert

宏原型:

macro_rules! assert {
    ($cond:expr $(,)?) => { ... };
    ($cond:expr, $($arg:tt)+) => { ... };
}

在运行时断言bool表达式为真。

fn main() {
    assert!(true);
    let a = 1;
    let b = 2;
    assert!(a + b == 3);
    // 使用自定义的格式输出断言信息
    assert!(1 + 1 == 3, "a = {} ,b = {},but now error is = {}", 1, 1, 3);
}

输出如下:

thread 'main' panicked at 'a = 1 ,b = 1,but now error is = 3', src/main.rs:2:5

需要注意的是,assert在release和debug版本都会输出,如果需要release版本禁用则可以使用debug_assert!宏。

assert_eq

判断表达式是否相等,和assert一样可以定义输出格式,等于的比较需要实现。宏原型:

macro_rules! assert_eq {
    ($left:expr, $right:expr $(,)?) => { ... };
    ($left:expr, $right:expr, $($arg:tt)+) => { ... };
}

示例代码:

fn main() {
    assert_eq!(1 + 1, 2);
    assert_eq!(1, 2, "we are testing addition with {} and {}", 1, 1);
}

assert_ne

对比两个表达式是否不相等(实现PartialEq),和assert一样可以定义panic格式,
assert_ne宏定义:

macro_rules! assert_ne {
    ($left:expr, $right:expr $(,)?) => { ... };
    ($left:expr, $right:expr, $($arg:tt)+) => { ... };
}

输出示例:

fn main() {
    assert_eq!(1, 2, "1 和 2 本身并不相等");
}

输出:


thread 'main' panicked at 'assertion failed: `(left == right)`
  left: `1`,
 right: `2`: 12 本身并不相等', src/main.rs:2:5

cfg

编译期计算表达式,宏定义:

macro_rules! cfg {
    ($($cfg:tt)*) => { ... };
}

#[cfg]用于提供编译期计算表达式的值。cfg!#[cfg]不同,不移除任何代码仅仅计算表达式的bool值是true还是false。所有的分支都需要能被计算。使用demo:

{
    let os = if cfg!(target_os = "macos") || cfg!(target_os = "linux") {
        "unix家族系统"
    } else {
        "windows"
    };
    println!("当前系统为:{}", os);
}

column

用宏主要用于调试代码时提供列号。示例:

fn main(){
    let a = ("foobar", column!()).1;
    let b = ("人之初性本善", column!()).1;
    let c = ("f̅o̅o̅b̅a̅r̅", column!()).1; // Uses combining overline (U+0305)
    let d = ("hello", column!());
    println!("内容:{} 位置 = {}", d.0, d.1);
    println!("a = {} b = {} c = {}", a, b, c);

    assert_eq!(a, b);
    assert_ne!(b, c);
}

compile_error

这歌宏主要用于条件编译时提供更好的错误信息,宏定义如:

下面的代码中,我们定义了一个宏,give_me_foo_or_bar,如果传入的参数为foo或者bar则表达式为{}否则报错:”This macro only accepts foo or bar“。

macro_rules! give_me_foo_or_bar {
    (foo) => {};
    (bar) => {};
    ($x:ident) => {
        compile_error!("This macro only accepts `foo` or `bar`");
    };
}

fn main() {
    give_me_foo_or_bar!(neither);
}

通过cfg控制编译输出:

#[cfg(not(any(feature = "foo", feature = "bar")))]
compile_error!("Either feature \"foo\" or \"bar\" must be enabled for this crate.");

其中any表示任何一个为false则为false,not(any(feature = "foo", feature = "bar"))表示存在foo或者bar为false。

cancat

连接字面量为静态字符串切片,宏定义:

macro_rules! concat {
    ($($e:expr),* $(,)?) => { ... };
}

concat示例:

fn main() {
    let s = concat!("test", 10, 'b', true, 1.23);
    assert_eq!(s, "test10btrue1.23");
}

dbg

打印同时返回表达式的值。宏定义:

macro_rules! dbg {
    () => { ... };
    ($val:expr $(,)?) => { ... };
    ($($val:expr),+ $(,)?) => { ... };
}

debug函数:


fn main() {
    fn factorial(n: u32) -> u32 {
        if dbg!(n <= 1) {
            dbg!(1)
        } else {
            dbg!(n * factorial(n - 1))
        }
    }

    dbg!(factorial(4));
}

上面的代码中我们对,阶乘计算函数检查,输入参数为4的时候,走判断n的分支,4<=1为false,则输出结果,接着走else分支调用4factorial(3),将结果给dbg计算,然后对factorial(3)继续计算,反复如此到对factorial(1)的计算,此时if分支满足,进入内部输出1 = 1,然后计算`nfactorial(n-1)`,直到输出所有的计算为止,输出:


[src/main.rs:3] n <= 1 = false
[src/main.rs:3] n <= 1 = false
[src/main.rs:3] n <= 1 = false
[src/main.rs:3] n <= 1 = true
[src/main.rs:4] 1 = 1
[src/main.rs:6] n * factorial(n - 1) = 2
[src/main.rs:6] n * factorial(n - 1) = 6
[src/main.rs:6] n * factorial(n - 1) = 24
[src/main.rs:10] factorial(4) = 24

移动、多输入示例:

fn main() {
    #[derive(Debug)]
    struct NoCopy(usize);

    let a = NoCopy(42);
    let _ = dbg!(a); // <-- `a` 已经被dbg移走
                     //    let _ = dbg!(a); // <-- `a` a的生命周期已经消失,再次move编译报错; error!
    assert_eq!(dbg!(1usize, 2u32), (1, 2)); //多参数将被当做tuple
    assert_eq!(1, dbg!(1u32,)); //tuple中单个参数依然被当做单个元素
}

debug_assert

表达式为真则断言bool,如果表达式不能计算则panic,类似于assert!,他也能提供自定义panic消息。其在debug代码中会出现

fn some_function() -> bool {
    return true;
}
fn main() {
    debug_assert!(true);
    debug_assert!(some_function());
    let a = 1;
    let b = 2;
    debug_assert!(1 + 2 == 4, "a = {} b = {} but add result = {}", a, b, a + b);
}

输出:

thread 'main' panicked at 'a = 1 b = 2 but add result = 3', src/main.rs:9:5
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace

debug_assert_eq

断言两个表达式的值是否相等。如果生成的代码不是优化代码,则其输出。除非人为给编译器加上-C debug-assertions

fn main() {
    let a = vec![1, 2, 3];
    let b = vec![1, 2, 4];
    debug_assert_eq!(a, b);
}

输出:

fn main() {
    let a = vec![1, 2, 3];
    let b = vec![1, 2, 4];
    debug_assert_eq!(a, b);
}

debug_assert_ne

判断表达式是否不相等,如果生成的代码不是优化代码,则其输出,除非人为给编译器加上-C debug-assertions

fn main() {
    let a = vec![1, 2, 3];
    let b = vec![1, 2, 4];
    debug_assert_ne!(a, b);
}

env

编译时展开环境变量生成&'static str。如果你想获取其值,可以通过std::env::var获取,如果环境变量没有定义则会产生编译错误,除非使用option_env!

fn main() {
    let path: &'static str = env!("HOME");
    println!("Home is {path}");
    //let doc: &'static str = env!("documentation", "what's that?!");//nightly支持自定义错误输出
}

eprint

print!功能类似,但是她输出信息到标准错误流。一般仅仅用于输出错误的场景,输出失败会产生panic。。

fn main() {
    eprint!("错误产生:(标准错误流)");
    print!("错误产生:(标准输出流)")
}

默认grep获取标准输出流的结果,所以下面的操作并不会匹配错误留的错误:

cargo run |grep 错误

eprintln

和eprint功能相同,输出换行。

定位文件的宏

fn main() {
    let filename = file!();
    println!("Current file {filename}");
}

输出:

Current file src/main.rs

format

创建一个格式化的字符串,接受格式化的字符串字面量{}用对应的字符串替代。

fn main() {
    let fname = "main";
    let mesg = format!("函数:{fname} cost time:{}s", "10");
    println!("{}", mesg);
}

输出:

函数:main cost time:10s

format_args

任何实现了Display的值都可以传入此宏。用于格式化字符串:

use std::fmt;
fn main() {
    let s = fmt::format(format_args!("hello {}", "world"));
    assert_eq!(s, format!("hello {}", "world"));
}

include

解析文件为表达式,对多文件的rust项目,此宏可能不查找文件,多文件使用modules。文件以相对路径的形式被定位,提供的路径在编译期被解释,windows下\在unix下将没有作用。其有两个关键作用:包含多个分开的文件和build.rs的包含。使用include宏的时候包含文件需要时可用的rust语法。例如:#![doc = include_str!("...")][doc = include_str!("...")]包含文档或者markdown文件。

fn main() {
    let my_string = include!("test.in");
    assert_eq!("22", my_string);
    println!("{my_string}");
}

这里的test.in里面为

'2'+'2'

这个表达式会被计算将结果返回给my_string。

include_bytes

和include类似,不同是文件是文件内容会被解释为bytes数组。生成的表达式为&static' [u8,N]。spanish.in文件

adiós
fn main() {
    let bytes = include_bytes!("spanish.in");
    assert_eq!(bytes, b"adi\xc3\xb3s\n");
    print!("{}", String::from_utf8_lossy(bytes));
}

include_str

包含utf-8编码的文件转换为字符串。

line

返回当前的调用行。

fn main() {
    let mesg = line!();
    println!("调用行:{mesg}");
}

输出:

调用行:2

matches

返回两个表达式是否匹配。

fn main() {
    let foo = 'f';
    assert!(matches!(foo, 'A'..='Z' | 'a'..='z'));

    let bar = Some(4);
    assert!(matches!(bar, Some(x) if x > 2));
}

扩展一个字符串表示当前mudule路径。

mod test {
    pub fn foo() {
        assert!(module_path!().ends_with("test"));
    }
}

fn main() {
    test::foo();
}

option_env

编译器获取环境变量为字符串,和env不同在于,环境变量不存在的时候返回None。

fn main() {
    let key: Option<&'static str> = option_env!("SECRET_KEY");
    println!("the secret key might be: {key:?}");
}

输出:


the secret key might be: None

panic

终止当前程序提供错误信息。

fn main() {
    panic!("this is a terrible mistake!");
    panic!("this is a {} {message}", "fancy", message = "message");
    std::panic::panic_any(4); // panic with the value of 4 to be collected elsewhere
}

print

输出信息到标准输出流,如果需要刷新流需要调用io::stdout().flash(),其每次调用的时候都会锁住标准输出流,循环输出的时候可能会成为性能瓶颈,为了避免这总情况可以使用io::stdout().lock()

use std::io::{self, Write};
fn main() {

    print!("this ");
    print!("will ");
    print!("be ");
    print!("on ");
    print!("the ");
    print!("same ");
    print!("line ");

    io::stdout().flush().unwrap();

    print!("this string has a newline, why not choose println! instead?\n");

    io::stdout().flush().unwrap();
}

println

和println一样,输出的时候包含换行符。

stringify

化简传入stringify的所有标记生成一个&'static str

thread_local

生成一个新的线程本地key。

todo

用于定义一段未实现的代码。运行未实现代码会产生panic。

thread 'main' panicked at 'not yet implemented', src/main.rs:11:9

unimplemented

表示未实现代码

trait Foo {
    fn foo(&self);
    fn bar(&self);
}
struct MyStruct;
impl Foo for MyStruct {
    fn foo(&self) {
        println!("foo implement");
    }
    fn bar(&self) {
        unimplemented!();
    }
}

fn main() {
    let a = MyStruct;
    a.bar();
}

unreachable

用于决定某段代码是否能走到。例如:

  1. 匹配条件分支
  2. 循环的动态结束
  3. 迭代器动态结束
    如果目的代码没有走到则panic错误。
fn divide_by_three(x: u32) -> u32 {
    // one of the poorest implementations of x/3
    for i in 0.. {
        if 3 * i < i {
            panic!("u32 overflow");
        }
        if x < 3 * i {
            return i - 1;
        }
    }
    unreachable!("The loop should always return");
}
fn main() {
    divide_by_three(10);
}

vec

创建一个包含参数的向量

write

写格式化的数据到buffer。

writeln

一行行写入数据到buffer。


   转载规则


《Rust基础(常见的标准宏)》 bleedingfight 采用 知识共享署名 4.0 国际许可协议 进行许可。
 上一篇
Rust基础(文件系统fs) Rust基础(文件系统fs)
文件系统中常见的包 DirBuilder:创建拥有丰富特性的目录的构建器。 DirEntry:ReadDir迭代器返回的条目。 File:访问文件系统上的文件的对象。 FileType:代表访问文件类型的结构。它由Metadata::fil
2023-09-13
下一篇 
Rust基础(Option) Rust基础(Option)
Option 表示值,如果有值则Some包装其值,否则返回None。 fn divide(a: f32, b: f32) -> Option<f32> { if b == 0.0f32 { None }
  目录