无法将字符串拆分为具有明确生命周期的字符串切片,因为该字符串的存活时间不够长 [英] Cannot split a string into string slices with explicit lifetimes because the string does not live long enough

查看:51
本文介绍了无法将字符串拆分为具有明确生命周期的字符串切片,因为该字符串的存活时间不够长的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在编写一个库,它应该从实现 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.我使用 expectmatch 处理错误,从而解压 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.

  1. 如何修复此错误 - 尝试数小时后仍无法正常工作.

  1. 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 Strings is a bad idea, sure, just like allocating too much is a bad idea in any language.

这篇关于无法将字符串拆分为具有明确生命周期的字符串切片,因为该字符串的存活时间不够长的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆