【问题标题】:Is there a more idiomatic way to use traits like `io::Read` with lifetimes?有没有更惯用的方式来使用像 `io::Read` 这样的特征和生命周期?
【发布时间】:2026-01-17 18:10:01
【问题描述】:

我有一个公共特征Parser,它定义了一个外部接口。然后我有一个私有的 ParserImpl 结构来实现这些方法(实际上,我有几个实现,这就是使用 trait 进行抽象的想法)。

use std::io;

pub trait Parser {
    // ...omitted
}

struct ParserImpl<R: io::Read> {
    // ...omitted
    stream: R,
}

impl<R: io::Read> ParserImpl<R> {
    // ...methods
    fn new(stream: R) -> ParserImpl<R> {
        ParserImpl {
            // ...omitted
            stream: stream,
        }
    }
}

impl<R: io::Read> Parser for ParserImpl<R> {
    // ...methods
}

为了创建解析器实例,我使用了一个函数来隐藏ParserImpl

pub fn make_parser<'a, R>(stream: R) -> Box<Parser + 'a>
where
    R: io::Read + 'a,
{
    Box::new(ParserImpl::new(stream))
}

这一切都很好......而且它有效......但是make_parser函数让我很困扰。我觉得必须有一种更简单的方法来解决这个问题,并且好像我错过了一些重要的东西,因为每当使用像 io::Read 这样的特征来抽象出数据源时,这似乎是一个潜在的陷阱。

我了解指定生命周期的必要性 (Parameter type may not live long enough?),但我有点难以理解我是否既可以拥有干净简单的界面,又可以使用像 io::Read 这样的特征。

是否有一种“更简洁”或者更惯用的方式来使用我所缺少的 io::Read 之类的特征?如果不是,那没关系,但我对 Rust 还很陌生,当我编写上述函数时,我一直在想“这不可能……”

为了使这个示例可以运行,这里有一个main

fn main() {
    use std::fs;
    let file: fs::File = fs::File::open("blabby.txt").unwrap();
    let parser = make_parser(file);
}

【问题讨论】:

  • 我使用了一个函数来隐藏ParserImpl——你为什么认为在这种情况下这样做很有价值? 这似乎是一个潜在的陷阱——你认为会发生什么样的陷阱?
  • 这是简化版。有多个解析器实现,它们都呈现相同的Parser 接口。其余代码需要与所使用的特定解析器无关。
  • 你知道你可以implement a trait for a box of that trait吗?那么这个函数不必是执行装箱的函数。

标签: rust lifetime


【解决方案1】:

编写具有该含义的代码的惯用方式,但您可能不想要该含义。

例如,如果您不需要创建一个装箱的 trait 对象,您可以直接返回参数化的值,或者在这种情况下只使用ParserImpl::new 的结果。这是我的默认表单,直到我知道我需要某个 trait 对象提供的动态调度。

您也可以要求 'static 生命周期而不是引入新的生命周期 'a,但这会减少您可以传递给 make_parser 的允许类型的范围:

pub fn make_parser<R>(stream: R) -> Box<Parser>
where
    R: io::Read + 'static,
{
    Box::new(ParserImpl::new(stream))
}

【讨论】:

  • @StacyProwell 也许这个问题真的是*.com/q/27535289/155423 的重复?
  • 不确定如何直接返回值;我不需要指定生命周期吗?
  • 啊!是的,“让编译器指导你”。请记住,我来自 C++,那里的编译器旨在积极地讨厌你。
  • @StacyProwell 如果您认为这是一个更全面的答案,我们鼓励您不接受此问题,然后您可以将您的问题标记为该问题的副本。
最近更新 更多