#format #string-formatting #concat #no-std

无std const_format_proc_macros

const_format 包的实现细节

13个版本

0.2.32 2023年10月14日
0.2.31 2023年5月29日
0.2.29 2022年10月11日
0.2.22 2021年10月14日
0.2.8 2020年10月7日

#1513文本处理

Download history 217981/week @ 2024-03-14 233004/week @ 2024-03-21 251885/week @ 2024-03-28 241130/week @ 2024-04-04 256294/week @ 2024-04-11 269634/week @ 2024-04-18 253899/week @ 2024-04-25 259487/week @ 2024-05-02 248395/week @ 2024-05-09 261438/week @ 2024-05-16 264075/week @ 2024-05-23 297369/week @ 2024-05-30 279820/week @ 2024-06-06 278655/week @ 2024-06-13 269137/week @ 2024-06-20 247086/week @ 2024-06-27

1,137,419 每月下载量
827 个crate中使用了(通过 const_format

Zlib 许可证

110KB
3K SLoC

Rust crates-io api-docs

编译时字符串格式化。

此crate提供用于编译时格式化字符串的类型和宏。

Rust版本

某些功能需要各种稳定的Rust版本,而其他功能则需要Rust nightly版本,下面的部分描述了每个版本可用的功能。

Rust 1.57.0

以下宏在Rust 1.57.0中可用

  • concatcp:将 integersboolchar&str 常量连接成一个 &'static str 常量。

  • formatcp:类似于 format 的格式化,它接受 integersboolchar&str 常量,并输出一个 &'static str 常量。

  • str_get:索引一个 &'static str 常量,当索引超出范围时返回 None

  • str_index:索引一个常量字符串。

  • str_repeat:通过重复一个常量字符串来创建一个新的常量字符串。

  • str_splice:替换一个常量字符串中的子字符串。

  • map_ascii_case:将一个常量字符串转换为不同的大小写风格,由一个 Case 参数决定。

  • str_replace:将一个常量字符串中所有模式实例替换为另一个常量字符串。

"assertcp" 功能启用 assertcpassertcp_eqassertcp_ne 宏。这些宏类似于标准库 assert 宏,但在编译时评估,限制是它们只能有原始类型作为参数(就像 concatcpformatcp)。

Rust 1.64.0

"rust_1_64" 功能启用这些宏

Rust nightly

通过启用 "fmt" 功能,您可以使用类似 std::fmt 的 API。

这需要 nightly 编译器,因为它在 const fn 中使用可变引用,这在撰写这些文档时尚未稳定。

此包的所有其他功能都是在 const_format::fmt API 的基础上实现的。

  • concatc:将许多标准库和用户定义的类型连接到一个常量字符串。

  • formatc:类似于 format 的宏,可以将许多标准库和用户定义的类型格式化到一个常量字符串。

  • writec:类似于 write 的宏,可以将许多标准库和用户定义的类型格式化为实现 WriteMarker 的类型。

"derive" 功能启用了 ConstDebug 宏和 "fmt" 功能。
ConstDebug 继承了 FormatMarker 特性,并实现了一个用于编译时调试格式化的内在方法 const_debug_fmt

"assertc" 功能启用了 assertcassertc_eqassertc_ne 宏和 "fmt" 功能。
这些宏类似于标准库 assert 宏,但会在编译时进行评估。

示例

原始类型的连接

use const_format::concatcp;

const NAME: &str = "Bob";
const FOO: &str = concatcp!(NAME, ", age ", 21u8,"!");

assert_eq!(FOO, "Bob, age 21!");

格式化原始类型

use const_format::formatcp;

const NAME: &str = "John";

const FOO: &str = formatcp!("{NAME}, age {}!", compute_age(NAME));

assert_eq!(FOO, "John, age 24!");

const fn compute_age(s: &str) -> usize { s.len() * 6 }

格式化自定义类型

此示例演示了如何使用 ConstDebug 继承宏,然后将类型格式化为 &'static str 常量。

此示例需要 Rust 夜间版本,并且需要 "derive" 功能。

#![feature(const_mut_refs)]

use const_format::{ConstDebug, formatc};

#[derive(ConstDebug)]
struct Message{
    ip: [Octet; 4],
    value: &'static str,
}

#[derive(ConstDebug)]
struct Octet(u8);

const MSG: Message = Message{
    ip: [Octet(127), Octet(0), Octet(0), Octet(1)],
    value: "Hello, World!",
};

const FOO: &str = formatc!("{:?}", MSG);

assert_eq!(
    FOO,
    "Message { ip: [Octet(127), Octet(0), Octet(0), Octet(1)], value: \"Hello, World!\" }"
);

格式化的 const 断言

此示例演示了如何使用 assertcp_ne 宏进行编译时不等式断言,并带有格式化的错误消息。

此功能需要 "assertcp" 功能。

use const_format::assertcp_ne;

macro_rules! check_valid_pizza{
    ($user:expr, $topping:expr) => {
        assertcp_ne!(
            $topping,
            "pineapple",
            "You can't put pineapple on pizza, {}",
            $user,
        );
    }
}

check_valid_pizza!("John", "salami");
check_valid_pizza!("Dave", "sausage");
check_valid_pizza!("Bob", "pineapple");

这是编译器的输出

error[E0080]: evaluation of constant value failed
  --> src/lib.rs:178:27
   |
20 | check_valid_pizza!("Bob", "pineapple");
   |                           ^^^^^^^^^^^ the evaluated program panicked at '
assertion failed: `(left != right)`
 left: `"pineapple"`
right: `"pineapple"`
You can't put pineapple on pizza, Bob
', src/lib.rs:20:27


限制

const_format 中的所有宏都有以下限制

  • 扩展为 &'static str 的格式化宏只能使用具体类型的常量,因此虽然 Type::<u8>::FOO 参数是合适的,但 Type::<T>::FOO 就不合适了(T 是一个类型参数)。

  • 整数参数必须具有可从上下文推断的类型,有关更多详细信息,请参阅 整数参数部分

  • 它们不能用于需要字符串字面量的地方。所以 #[doc = "foobar"] 不能替换为 #[doc = concatcp!("foo", "bar") ]

整数参数

整数参数必须有从上下文中可推断的类型。因此,如果只传递一个整数字面量,它必须有一个后缀。

可编译示例

const N: u32 = 1;
assert_eq!(const_format::concatcp!(N + 1, 2 + N), "23");

assert_eq!(const_format::concatcp!(2u32, 2 + 1u8, 3u8 + 1), "234");

不可编译示例

assert_eq!(const_format::concatcp!(1 + 1, 2 + 1), "23");

计划

目前没有。

重命名crate

当crate重命名时,所有来自 const_format 的函数式宏都可以使用。

ConstDebug derive宏有一个#[cdeb(crate = "foo::bar")]属性,用于指示在哪里可以找到const_format crate。

在Cargo.toml文件中重命名const_format crate的示例

[dependencies]
cfmt = {version = "0.*", package = "const_format"}

Cargo功能

  • "fmt":启用类似std::fmt的API,因为使用了在const fn中的可变引用,所以需要Rust nightly版本。
    此功能包括formatc/writec格式化宏。

  • "derive":需要Rust nightly版本,隐含了"fmt"功能,提供ConstDebug derive宏,在编译时格式化用户定义的类型。
    此功能隐式使用syn crate,因此带有该功能的编译会比不带该功能的编译慢一些。

  • "assertc":需要Rust nightly版本,隐含了"fmt"功能,启用了assertcassertc_eqassertc_ne断言宏。
    此功能之前命名为"assert",但为了避免与"assertcp"功能混淆,已将其重命名。

  • "assertcp":启用 assertcpassertcp_eqassertcp_ne 断言宏。

  • "rust_1_64":启用 str_split 宏。允许 as_bytes_alt 方法以及 slice_up_to_len_alt 方法以常量时间运行,而不是线性时间(与切片被截断的部分成比例)。

无标准支持

const_format 无条件地 #![no_std],可以在Rust能使用的任何地方使用。

最低支持的Rust版本

const_format 需要 Rust 1.57.0。

需要较新版本的Rust或夜间编译器的特性需要通过cargo features显式启用。

依赖项

~73–480KB
~11K SLoC