我正在编写一个程序来从日志文件(文本格式)中提取信息。总体流量是
String
ParsedLine
结构,该结构从该行借用多个字符串片段(有些使用Cow
)ParsedLine
编写CSV记录。到目前为止,这种情况一直很好,但是我遇到了一个我不了解的问题,我认为这与生命周期或数据流分析有关。问题出在我试图做的一个小的重构上。
我具有此功能,该功能有效:
fn process_line(columns: &[Column], line: String, writer: &mut Writer<File>) {
let parsed_line = ParsedLine::new(&line);
if parsed_line.is_err() {
let data = vec![""];
writer.write_record(&data).expect("Writing a CSV record should always succeed.");
return;
}
let parsed_line = parsed_line.unwrap();
// let data = output::make_output_record(&parsed_line, columns);
// The below code works. But if I try to pull it out into a separate function
// Rust will not compile it.
let mut data = Vec::new();
for column in columns {
match column.name.as_str() {
config::LOG_DATE => data.push(parsed_line.log_date),
config::LOG_LEVEL => data.push(parsed_line.log_level),
config::MESSAGE => data.push(&parsed_line.message),
_ => {
let ci_comparer = UniCase::new(column.name.as_str());
match parsed_line.kvps.get(&ci_comparer) {
Some(val) => {
let x = val.as_ref();
data.push(x);
},
None => data.push(""),
}
},
}
}
writer.write_record(&data).expect("Writing a CSV record should always succeed.");
}
但是我想提取一些将data
构造为单独函数的代码,以便可以更轻松地对其进行测试。这是该函数:
pub fn make_output_record<'p, 't, 'c>(parsed_line: &'p ParsedLine<'t>, columns: &'c [Column]) -> Vec<&'t str> {
let mut data = Vec::new();
for column in columns {
match column.name.as_str() {
config::LOG_DATE => data.push(parsed_line.log_date),
config::LOG_LEVEL => data.push(parsed_line.log_level),
config::MESSAGE => data.push(&parsed_line.message),
_ => {
let ci_comparer = UniCase::new(column.name.as_str());
match parsed_line.kvps.get(&ci_comparer) {
// This is the problem here. To make it explicit:
// val is a "&'t Cow<'t, str>" and x is "&'t str"
Some(val) => {
let x = val.as_ref();
data.push(x);
},
None => data.push(""),
}
},
}
}
data
}
我得到但不理解的错误是:
error[E0623]: lifetime mismatch
--> src/main.rs:201:5
|
177 | pub fn make_output_record<'p, 't, 'c>(parsed_line: &'p ParsedLine<'t>, columns: &'c [Column]) -> Vec<&'t str> {
| ------------ ------------
| |
| this parameter and the return type are declared with different lifetimes...
...
201 | data
| ^^^^ ...but data from `columns` is returned here
编译器认为返回的向量包含来自Columns
的信息,但是Columns
实际上仅用于获取列的名称,该列的名称随后用于在{{1 }} HashMap(kvps
用于使查找不区分大小写)。如果找到一个值,则将UniCase
添加到&str
。
所以我不明白为什么编译器会认为data
中的内容以Columns
结尾,因为在我看来data
只是用于驱动最终结果的一些元数据Columns
的内容,但没有出现在data
中。完成data
查找后,我们可能还不存在值kvps
。
我尝试了各种方法来解决此问题(包括为所有内容添加显式生存期,删除某些生存期并添加各种生存期的生存期规范),但似乎没有任何组合能够告诉编译器Columns
并非如此在Columns
中使用。
作为参考,这是data
的定义:
ParsedLine
请注意,我拒绝摆脱#[derive(Debug, Default, PartialEq, Eq)]
pub struct ParsedLine<'t> {
pub line: &'t str,
pub log_date: &'t str,
pub log_level: &'t str,
pub message: Cow<'t, str>,
pub kvps: HashMap<UniCase<&'t str>, Cow<'t, str>>
}
:我认为这可以解决问题,但是String分配的数量可能会增加20倍,我想避免这种情况。当前的程序速度惊人!
我怀疑问题实际上出在Cows
上,我需要给出密钥的寿命。不确定如何。
我的问题是
我明白这是一个相当长的问题。在本地处理代码可能更容易。它在Github上,并且错误可以通过以下方式重现:
UniCase<&'t str>
答案 0 :(得分:4)
从make_output_record
对process_line
的调用将推断make_output_record
的生存期参数。
pub fn make_output_record<'p>(parsed_line: &'p ParsedLine, columns: &'p [Column]) -> Vec<&'p str> {
这意味着'p
是生命周期,所有者将在process_line
的范围内生存(由于推断)。根据您的代码parsed_line
和columns
居住在'p
中。 'p
是返回值和参数的通用生存期。这就是为什么您的代码无法正常工作的原因,因为'p,'t,'c对于参数和返回值并不常见。
我在here中简化了您的代码,这是有效的版本,如果您将其他生命周期参数添加回make_output_record
,则可能会返回错误。