Written by: algebnaly
Date: 2026-02-12T09:10:51.000Z
在LLM的辅助下, 编写了如下的复杂rust代码, 像黑魔法一样, 最后我还是选择从实际的代码库中删去了它, 因为太难懂了, 放在这篇博客里留作纪念.
写得这么复杂的原因是我希望这段代码可以尽量通用, 输入可以是&str, 可以是bytes或者什么其他任何满足约束的类型。
pub struct Keywords<'a, T: ?Sized + 'a, const N: usize> {
keywords: [&'a T; N],
}
impl<'a, T: ?Sized + 'a, const N: usize> Keywords<'a, T, N> {
pub fn new(keywords: [&'a T; N]) -> Self {
Keywords { keywords }
}
}
impl<'a, I, T, const N: usize> Parser<I> for Keywords<'a, T, N>
where
T: ?Sized + 'a,
I: Input + Compare<&'a T>,
&'a T: Input + Clone,
<I as Input>::Item: AsChar,
{
type Output = ();
type Error = nom::error::Error<I>;
fn process<OM: nom::OutputMode>(
&mut self,
mut input: I,
) -> nom::PResult<OM, I, Self::Output, Self::Error> {
if N == 0 {
return Ok((input, OM::Output::bind(|| ())));
}
let (rest, _) = multispace0.process::<OM>(input)?;
input = rest;
let (rest, _) = tag(self.keywords[0]).process::<OM>(input)?;
input = rest;
for i in 1..N {
let (rest, _) = multispace1.process::<OM>(input)?;
input = rest;
let (rest, _) = tag(self.keywords[i]).process::<OM>(input)?;
input = rest;
}
Ok((input, <OM::Output as Mode>::bind(|| ())))
}
}
pub fn keywords<'a, T: ?Sized + 'a, const N: usize>(kws: [&'a T; N]) -> Keywords<'a, T, N> {
Keywords::new(kws)
}
这段代码的作用实际上非常简单, 就是创建一个parser, 用来吃掉给定的关键词序列, 并返回一个空元组。关键词序列的开头可以是一个或多个空格, 关键词之间至少被一个空格划分。
这里的主要困难是搞懂nom的OutputMode, 以及I, T这几个泛型参数各自代表什么, 各个parser对参数的约束之类的。
返回值使用<OM::Output as Mode>::bind(|| ())是因为nom 为了支持Emit和Check两种输出模式, Emit模式下会产生一个值, 所有bind中的闭包会被调用, 产生实际的输出.Check模式下可能不会调用闭包, 编译器可以进行性能优化。