自定义CSVParser结构体调用parse_csv方法时提示方法未找到的问题排查求助
自定义CSVParser结构体调用parse_csv方法时提示方法未找到的问题排查求助
各位大佬好,我最近在折腾一个自定义的CSV解析器,用来处理公司内部那种格式奇怪的CSV——不能改输入格式,里面有多行值,而且所有字段都用双引号包着。目前我已经用Logos实现了一个能用的tokenizer,能把输入文本解析成[Option<&'s str>; COLUMNS](COLUMNS是const泛型),但在写测试用例,把解析后的数组转换成具体业务类型的时候,遇到了一个特别诡异的编译错误:
error[E0599]: no method named `parse_csv` found for struct `CSVParser<'_, 6, BadBin<'_>, fn(...) -> ... {parse_item}>` in the current scope --> src\csv_parser.rs:424:45 | 118 | pub struct CSVParser<'f, const COLUMNS: usize, E: Error, F: CSVItemParser<'f, COLUMNS, E>> { | ------------------------------------------------------------------------------------------ method `parse_csv` not found for this struct ... 424 | expect!("").assert_debug_eq(&parser.parse_csv(SAMPLE_INPUT)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ method not found in `CSVParser<'_, 6, BadBin<'_>, fn(...) -> ... {parse_item}>` | = note: the method was found for - `csv_parser::CSVParser<'f, COLUMNS, E, F>`
编译器说方法parse_csv不存在于我的parser实例类型里,但又说这个方法确实存在于CSVParser<'f, COLUMNS, E, F>这个结构体上,我完全搞不懂这矛盾的提示是什么意思😵💫
下面是我代码里的核心部分(完整代码是个gist,这里只贴关键逻辑,注释可能调整了行号):
use std::error::Error; use core::marker::PhantomData; pub struct CSVParser<'f, const COLUMNS: usize, E: Error, F: CSVItemParser<'f, COLUMNS, E>> { field_labels: [&'f str; COLUMNS], item_parser: F, _error_marker: PhantomData<E>, } /// 用来把[Option<&'s str>; COLUMNS]转换成业务类型的Trait pub trait CSVItemParser<'f, const COLUMNS: usize, E: Error> { type Output<'s> where 'f: 's, Self: 's, E: 's; fn parse_item<'s>( &self, row: CSVRow<'f, 's, COLUMNS, E>, ) -> Result<Self::Output<'s>, ParseError<'f, 's, COLUMNS, E>> where Self: Sized + 's, E: 's; } // 给闭包/函数实现这个Trait,方便直接传解析逻辑 impl<'f, const COLUMNS: usize, E, F, T> CSVItemParser<'f, COLUMNS, E> for F where E: Error, F: for<'s> Fn(CSVRow<'f, 's, COLUMNS, E>) -> Result<T, ParseError<'f, 's, COLUMNS, E>>, { type Output<'s> = T where 'f: 's, Self: 's, E: 's; fn parse_item<'s>( &self, row: CSVRow<'f, 's, COLUMNS, E>, ) -> Result<Self::Output<'s>, ParseError<'f, 's, COLUMNS, E>> where Self: Sized + 's, E: 's, { self(row) } } /// 封装行数据和字段标签的辅助结构体,有一些方便取值的方法 pub struct CSVRow<'f: 's, 's, const COLUMNS: usize, E: Error> { fields: &'f [&'f str; COLUMNS], values: [Option<&'s str>; COLUMNS], _error_marker: PhantomData<E>, } // CSVParser的实现块,里面定义了parse_csv方法 impl<'f, const COLUMNS: usize, E: Error, F: CSVItemParser<'f, COLUMNS, E>> CSVParser<'f, COLUMNS, E, F> { pub fn new(fields: [&'f str; COLUMNS], parse_item: F) -> Self { Self { field_labels: fields, item_parser: parse_item, _error_marker: PhantomData, } } // 就是这个编译器说找不到的方法 pub fn parse_csv<'s>( &'f self, source: &'s str, ) -> Result<Vec<F::Output<'s>>, ParseError<'f, 's, COLUMNS, E>> { let mut lex = Token::lexer(source); let mut items = Vec::new(); let mut tok = match lex.next() { Some(Ok(Token::Header)) => match lex.next() { Some(Ok(Token::EOL)) => None, Some(Ok(tok)) => Some(tok), Some(Err(_)) => Err(ParseError::TokenError(lex.slice()))?, None => return Ok(items), }, Some(Ok(tok)) => Some(tok), Some(Err(_)) => Err(ParseError::TokenError(lex.slice()))?, None => return Ok(items), }; loop { let fields = self.parse_line(&mut lex, tok.take())?; items.push(self.item_parser.parse_item(fields)?); tok = match lex.next() { Some(Ok(t)) => Some(t), Some(Err(_)) => Err(ParseError::TokenError(lex.slice()))?, None => return Ok(items), } } } // 这里省略了parse_line的具体实现,它负责把token流转换成CSVRow fn parse_line<'s>(&self, lex: &mut logos::Lexer<'s, Token<'s>>, tok: Option<Token<'s>>) -> Result<CSVRow<'f, 's, COLUMNS, E>, ParseError<'f, 's, COLUMNS, E>> { // 具体解析逻辑... } } // 测试用的错误类型和解析函数 #[derive(Debug)] struct BadBin<'a>(&'a str); impl<'a> Error for BadBin<'a> {} impl<'a> std::fmt::Display for BadBin<'a> { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { write!(f, "BadBin: {}", self.0) } } fn parse_item<'f, 's>(row: CSVRow<'f, 's, 6, BadBin<'f>>) -> Result<(), ParseError<'f, 's, 6, BadBin<'f>>> { // 业务解析逻辑... Ok(()) } // 测试代码 const SAMPLE_INPUT: &str = "\"col1\",\"col2\",\"col3\",\"col4\",\"col5\",\"col6\"\n\"val1\",\"val2\",\"val3\",\"val4\",\"val5\",\"val6\""; #[test] fn test_csv_parse() { let parser = CSVParser::new(["col1","col2","col3","col4","col5","col6"], parse_item); expect_test::expect!("").assert_debug_eq(&parser.parse_csv(SAMPLE_INPUT)); }
我的疑惑
我反复对比了结构体的泛型参数和impl块的参数,看起来完全一致啊?编译器提示的存在方法的类型CSVParser<'f, COLUMNS, E, F>和我的实例类型CSVParser<'_, 6, BadBin<'_>, fn(...) -> ... {parse_item}>,泛型参数都是匹配的,只是生命周期用了占位符'_而已。
我怀疑是不是生命周期约束太严格了?比如parse_csv方法里的&'f self,是不是应该改成&self?或者是CSVItemParser trait里的关联类型Output<'s>的生命周期约束有问题?
有没有大佬能帮我看看问题出在哪?卡了快一下午了,实在没头绪😭
内容来源于stack exchange




