我正在尝试做一些相对简单的事情:获取第 n 个字符不同的字符串。所以,举个例子,
let x = "XXXX";
let n = 2;
// code
"XXOX"
我很清楚这可以通过 2 个循环来完成,例如:
let n = 5;
let c = 2;
let mut string = String::new();
for i in 0..c {
string.push('X');
}
string.push('O');
for i in c..n {
string.push('X')
}
但我想知道是否有更好的方法来做到这一点。我的字符串在编译时是恒定的,所以我可以使用宏,但问题是填充似乎没有按照我想要的方式工作,因为它只向一个方向移动。
如果您需要的是一个带有
&str
字符的常量 len
,其中除了第 n
个字符之外所有字符都相同,那么您可以通过一些代码和声明性宏来实现此目的。
/// constified `char::encode_utf8` from the standard library
/// https://doc.rust-lang.org/1.77.1/src/core/char/methods.rs.html#1769
const fn encode(c: char) -> ([u8; 4], usize) {
const TAG_CONT: u8 = 0b1000_0000;
const TAG_TWO_B: u8 = 0b1100_0000;
const TAG_THREE_B: u8 = 0b1110_0000;
const TAG_FOUR_B: u8 = 0b1111_0000;
let mut dst = [0; 4];
let code = c as u32;
let len = c.len_utf8();
match len {
1 => {
dst[0] = code as u8;
}
2 => {
dst[0] = (code >> 6 & 0x1F) as u8 | TAG_TWO_B;
dst[1] = (code & 0x3F) as u8 | TAG_CONT;
}
3 => {
dst[0] = (code >> 12 & 0x0F) as u8 | TAG_THREE_B;
dst[1] = (code >> 6 & 0x3F) as u8 | TAG_CONT;
dst[2] = (code & 0x3F) as u8 | TAG_CONT;
}
4 => {
dst[0] = (code >> 18 & 0x07) as u8 | TAG_FOUR_B;
dst[1] = (code >> 12 & 0x3F) as u8 | TAG_CONT;
dst[2] = (code >> 6 & 0x3F) as u8 | TAG_CONT;
dst[3] = (code & 0x3F) as u8 | TAG_CONT;
}
_ => unreachable!(),
};
(dst, len)
}
const fn make<const TOTAL_LEN: usize>(
n: usize,
len: usize,
padding: char,
different: char,
) -> [u8; TOTAL_LEN] {
assert!(n < len, "n is larger than len");
let mut arr: [u8; TOTAL_LEN] = [0; TOTAL_LEN];
let (pad_encoded, pad_len) = encode(padding);
let (diff_encoded, diff_len) = encode(different);
assert!(
pad_len * (len - 1) + diff_len == TOTAL_LEN,
"lengths did not match"
);
let mut i = 0;
let mut byte = 0;
while i < n {
let mut j = 0;
while j < pad_len {
arr[byte] = pad_encoded[j];
j += 1;
byte += 1;
}
i += 1
}
let mut j = 0;
while j < diff_len {
arr[byte] = diff_encoded[j];
j += 1;
byte += 1;
}
i += 1;
while i < len {
let mut j = 0;
while j < pad_len {
arr[byte] = pad_encoded[j];
j += 1;
byte += 1;
}
i += 1
}
arr
}
macro_rules! nth_different_str {
($len:expr, $n:expr, $padding:expr, $different:expr) => {{
const TOTAL_LEN: usize = (($len - 1) * $padding.len_utf8()) + $different.len_utf8();
const ARR: [u8; TOTAL_LEN] = make($n, $len, $padding, $different);
let Ok(s) = std::str::from_utf8(&ARR) else {
unreachable!();
};
s
}};
}
pub const NTH_STR: &str = nth_different_str!(4, 2, 'X', 'O');