无法将字符串拆分为具有明确生命周期的字符串切片,因为该字符串的存活时间不够长 [英] Cannot split a string into string slices with explicit lifetimes because the string does not live long enough
问题描述
我正在编写一个库,它应该从实现 BufRead
特性的东西中读取;网络数据流、标准输入等.第一个函数应该从该读取器读取数据单元并返回一个填充结构,该结构主要填充 &'a str
值从帧解析电线.
I'm writing a library that should read from something implementing the BufRead
trait; a network data stream, standard input, etc. The first function is supposed to read a data unit from that reader and return a populated struct filled mostly with &'a str
values parsed from a frame from the wire.
这是一个最小版本:
mod mymod {
use std::io::prelude::*;
use std::io;
pub fn parse_frame<'a, T>(mut reader: T)
where
T: BufRead,
{
for line in reader.by_ref().lines() {
let line = line.expect("reading header line");
if line.len() == 0 {
// got empty line; done with header
break;
}
// split line
let splitted = line.splitn(2, ':');
let line_parts: Vec<&'a str> = splitted.collect();
println!("{} has value {}", line_parts[0], line_parts[1]);
}
// more reads down here, therefore the reader.by_ref() above
// (otherwise: use of moved value).
}
}
use std::io;
fn main() {
let stdin = io::stdin();
let locked = stdin.lock();
mymod::parse_frame(locked);
}
在尝试不同的解决方案后我无法修复的错误显示:
An error shows up which I cannot fix after trying different solutions:
error: `line` does not live long enough
--> src/main.rs:16:28
|
16 | let splitted = line.splitn(2, ':');
| ^^^^ does not live long enough
...
20 | }
| - borrowed value only lives until here
|
note: borrowed value must be valid for the lifetime 'a as defined on the body at 8:4...
--> src/main.rs:8:5
|
8 | / {
9 | | for line in reader.by_ref().lines() {
10 | | let line = line.expect("reading header line");
11 | | if line.len() == 0 {
... |
22 | | // (otherwise: use of moved value).
23 | | }
| |_____^
生命周期 'a
是在结构体和数据管理器结构的实现上定义的,因为 &str
需要明确的生命周期.这些代码部分作为最小示例的一部分被删除.
The lifetime 'a
is defined on a struct and implementation of a data keeper structure because the &str
requires an explicit lifetime. These code parts were removed as part of the minimal example.
BufReader
有一个 lines()
方法,它返回 Result
.我使用 expect
或 match
处理错误,从而解压 Result
以便程序现在拥有裸露的 String
.然后将多次执行此操作以填充数据结构.
BufReader
has a lines()
method which returns Result<String, Err>
. I handle errors using expect
or match
and thus unpack the Result
so that the program now has the bare String
. This will then be done multiple times to populate a data structure.
许多答案说unwrap
结果需要绑定到一个变量,否则它会丢失,因为它是一个临时值.但是我已经将解压后的 Result
值保存在变量 line
中,但仍然出现错误.
Many answers say that the unwrap
result needs to be bound to a variable otherwise it gets lost because it is a temporary value. But I already saved the unpacked Result
value in the variable line
and I still get the error.
如何修复此错误 - 尝试数小时后仍无法正常工作.
How to fix this error - could not get it working after hours trying.
在数据管理器结构中为 &str
做所有这些生命周期声明有意义吗?这将主要是一个只读数据结构,最多替换整个字段值.String
也可以使用,但是发现有文章说String
的性能比&str
低 - 并且会调用此帧解析器函数很多次,并且对性能至关重要.
Does it make sense to do all these lifetime declarations just for &str
in a data keeper struct? This will be mostly a readonly data structure, at most replacing whole field values. String
could also be used, but have found articles saying that String
has lower performance than &str
- and this frame parser function will be called many times and is performance-critical.
Stack Overflow 上也有类似的问题,但没有一个能完全回答这里的情况.
Similar questions exist on Stack Overflow, but none quite answers the situation here.
为了完整性和更好的理解,以下是关于为什么会出现生命周期问题的完整源代码的摘录:
For completeness and better understanding, following is an excerpt from complete source code as to why lifetime question came up:
数据结构声明:
// tuple
pub struct Header<'a>(pub &'a str, pub &'a str);
pub struct Frame<'a> {
pub frameType: String,
pub bodyType: &'a str,
pub port: &'a str,
pub headers: Vec<Header<'a>>,
pub body: Vec<u8>,
}
impl<'a> Frame<'a> {
pub fn marshal(&'a self) {
//TODO
println!("marshal!");
}
}
完整的函数定义:
pub fn parse_frame<'a, T>(mut reader: T) -> Result<Frame<'a>, io::Error> where T: BufRead {
推荐答案
您的问题可以简化为:
fn foo<'a>() {
let thing = String::from("a b");
let parts: Vec<&'a str> = thing.split(" ").collect();
}
您在函数中创建一个 String
,然后声明对该字符串的引用保证在生命周期 'a
内有效.不幸的是,生命周期'a
不在你的控制之下——函数的调用者可以选择生命周期是什么.这就是泛型参数的工作原理!
You create a String
inside your function, then declare that references to that string are guaranteed to live for the lifetime 'a
. Unfortunately, the lifetime 'a
isn't under your control — the caller of the function gets to pick what the lifetime is. That's how generic parameters work!
如果函数的调用者指定了 'static
生命周期,会发生什么?您的代码在运行时分配一个值,如何保证该值的生命周期甚至比 main
函数还长?不可能,这就是编译器报错的原因.
What would happen if the caller of the function specified the 'static
lifetime? How would it be possible for your code, which allocates a value at runtime, to guarantee that the value lives longer than even the main
function? It's not possible, which is why the compiler has reported an error.
一旦你获得了更多的经验,函数签名 fn foo<'a>()
会像红色警报一样跳出来 - 有一个通用参数 isn没有使用.这很可能意味着坏消息.
Once you've gained a bit more experience, the function signature fn foo<'a>()
will jump out at you like a red alert — there's a generic parameter that isn't used. That's most likely going to mean bad news.
返回一个填充结构,主要填充 &'a str
return a populated struct filled mostly with
&'a str
以当前的代码组织方式,您不可能做到这一点.引用必须指向某事.你没有为指向的价值观提供任何地方.您不能将分配的String
作为字符串切片返回.
You cannot possibly do this with the current organization of your code. References have to point to something. You are not providing anywhere for the pointed-at values to live. You cannot return an allocated String
as a string slice.
在跳转之前,您不能在同一个结构体中存储值和对该值的引用.
相反,您需要拆分创建 String
的代码和解析 &str
并返回更多 &str
的代码参考.这就是所有现有的零拷贝解析器的工作方式.你可以看看这些来寻找灵感.
Instead, you need to split the code that creates the String
and that which parses a &str
and returns more &str
references. That's how all the existing zero-copy parsers work. You could look at those for inspiration.
String
的性能低于 &str
不,它真的没有.创建大量无关的 String
是个坏主意,当然,就像在任何语言中分配太多都是个坏主意一样.
No, it really doesn't. Creating lots of extraneous String
s is a bad idea, sure, just like allocating too much is a bad idea in any language.
这篇关于无法将字符串拆分为具有明确生命周期的字符串切片,因为该字符串的存活时间不够长的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!