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. | |
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`: 1 和 2 本身并不相等', 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
}
输出信息到标准输出流,如果需要刷新流需要调用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
用于决定某段代码是否能走到。例如:
- 匹配条件分支
- 循环的动态结束
- 迭代器动态结束
如果目的代码没有走到则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。