使第 n 个字符不同的惯用方法

问题描述 投票:0回答:1

我正在尝试做一些相对简单的事情:获取第 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')
}

但我想知道是否有更好的方法来做到这一点。我的字符串在编译时是恒定的,所以我可以使用宏,但问题是填充似乎没有按照我想要的方式工作,因为它只向一个方向移动。

string rust
1个回答
0
投票

如果您需要的是一个带有

&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');

游乐场

© www.soinside.com 2019 - 2024. All rights reserved.