2017-06-20 56 views
-2

我正在编写一个应该从实现BufRead特征的东西读取的库;网络数据流,标准输入等。第一个函数应该从该读取器读取数据单元,并返回填充的结构,其中大部分填充了从导线帧中解析出的&'a str值。无法将字符串拆分为具有明确生命期的字符串切片,因为字符串没有足够长的生命

下面是一个最小的版本:

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); 
} 

错误显示出来,我可以尝试不同的解决方案后无法修复:

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需要明确的生命周期。这些代码部分作为最小示例的一部分被删除。

BufReader有一个lines()方法返回Result<String, Err>。我使用expectmatch来处理错误,因此解压缩Result,以便程序现在拥有裸机String。这将会多次完成以填充数据结构。

许多答案认为unwrap结果需要绑定到变量,否则它会因为它是一个临时值而丢失。但是我已经将解压后的Result值保存在变量line中,我仍然收到错误消息。

  1. 如何解决这个错误 - 经过数小时的尝试后无法工作。

  2. 在数据守护程序结构中为&str执行所有这些生命周期声明是否有意义?这将主要是一个只读数据结构,最多可取代整个字段值。 String也可以使用,但发现文章说String的性能比&str低 - 而且这个帧解析器函数将被调用很多次并且对性能至关重要。

Stack Overflow存在类似的问题,但没有一个能够完全回答这里的情况。

的完整性和更好的了解,下面是完整的源代码,为什么寿命问题就来了一个摘录:

数据结构声明:

// 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 { 
+1

''了'一辈子不'line'寿命相匹配。 https://play.rust-lang.org/?gist=3f510b31e0009af23d637c04660d4586&version=stable&backtrace=0。 – Stargateur

+0

哦,我的......这么简单的改变。我假定所有'''&str'''和'''Vec <&str>'''需要具有相同的生命周期,因此'''Vec <&'a str>'''。没想到*这*是麻烦的原因。你显然有一个训练有素的眼睛 - 非常感谢你,@星际之门! – ERnsTL

+2

不仅“a”不受限制,没有任何东西拥有从读取器获取的测试数据。你可能想使用String而不是非拥有字符串。 –

回答

2

您的问题可以简化为:

fn foo<'a>() { 
    let thing = String::from("a b"); 
    let parts: Vec<&'a str> = thing.split(" ").collect(); 
} 

您在函数内部创建了String,然后声明对该字符串的引用是保证生存期为'a。不幸的是,终生'a不受你控制 - 函数的调用者可以选择生命周期。这就是通用参数的工作原理!

如果函数的调用者指定了'static的生命期会发生什么?你的代码如何在运行时分配一个值来保证值的寿命甚至比main函数还要长?这是不可能的,这就是编译器报告错误的原因。

一旦你获得了一点经验,函数签名fn foo<'a>()会跳出在你喜欢的红色警报 - 有这么不使用一个泛型参数。这很可能意味着坏消息。


回报人口结构大多充满了&'a str

你不可能用你的代码目前的组织做到这一点。参考文献必须指向东西。你没有提供任何地方的尖锐价值的生活。你不能return an allocated String as a string slice

在你跳到它之前,没有你cannot store a value and a reference to that value in the same struct

相反,您需要拆分创建String的代码以及解析&str并返回更多&str引用的代码。这就是所有现有的零拷贝解析器的工作原理。你可以看看那些灵感。

String&str

没有性能较低,但事实并非如此。创建大量无关String是一个坏主意,当然,就像分配太多在任何语言中都是坏主意一样。

+0

问候@Shepmaster,感谢您的解释!将看看在Rust中编写的解析器。我不完全确定你写的是什么意思*引用必须指向某个*或*必须由某人拥有*:根据我的理解,一个'''&str'''指向一个字符串= region在内存中,然后它可以被附加到数据'''struct''''并且可以被返回到拥有/接收返回的结果数据结构的调用者。至少我的心智模式来自其他PL。 Rust似乎需要不同的思考方式。 Greetings – ERnsTL

+0

@ERnsTL Rust和其他语言在发生什么事情上没有太大的区别。不同之处在于Rust确保您的引用始终有效(与C或C++不同),并且不使用垃圾回收器(与Java,C#,Ruby等不同)。在像Java这样的语言中,每个“对象”实际上都是一种参考,而语言/虚拟机可以确保所提及的内容将随着需要而变化。你的代码试图在没有被引用的东西的情况下进行引用;这类似于没有房子的地址;访问该地址将导致坏事。 – Shepmaster

+0

再次感谢您的解释 - 这显然不足以理解Rust的一些语法;知道所有权制度是必须的,它会造成一切后果。在*锈病书*在*章节了解所有权*(引用,借用,切片)和*通用集合*(串和切片)解释为什么上面的代码不起作用。在第5章(2版草案)*结构*数据的所有权具有酷似上面在第10章的溶液的例子:*通用类型,性状和寿命*。学习Nom和教程等零拷贝解析器(例如* Rusty Buffers *)也有很大帮助。 – ERnsTL

0

也许下面的程序给出了其他人谁也还具有寿命他们的第一个问题的线索:

fn main() { 
    // using String und &str Slice 
    let my_str: String = "fire".to_owned(); 
    let returned_str: MyStruct = my_func_str(&my_str); 
    println!("Received return value: {ret}", ret = returned_str.version); 

    // using Vec<u8> und &[u8] Slice 
    let my_vec: Vec<u8> = "fire".to_owned().into_bytes(); 
    let returned_u8: MyStruct2 = my_func_vec(&my_vec); 
    println!("Received return value: {ret:?}", ret = returned_u8.version); 
} 


// using String -> str 
fn my_func_str<'a>(some_str: &'a str) -> MyStruct<'a> { 
    MyStruct { 
     version: &some_str[0..2], 
    } 
} 

struct MyStruct<'a> { 
    version: &'a str, 
} 


// using Vec<u8> -> & [u8] 
fn my_func_vec<'a>(some_vec: &'a Vec<u8>) -> MyStruct2<'a> { 
    MyStruct2 { 
     version: &some_vec[0..2], 
    } 
} 

struct MyStruct2<'a> { 
    version: &'a [u8], 
} 
相关问题