Rust参考在仍被借用时被放弃

11
问题:我在一个方法中收到了一个错误信息 "XXXXXXX 在还未归还的情况下被放弃",其中 XXXXXXX 有效期到方法的末尾(这是可以接受的),但 Rust 不必要地期望它能够与输入引用一样长寿,因为这些引用具有显式生命周期。
以下是一些设置信息:我有一个结构体,它可以迭代文本内容中找到的所有查询。
pub struct QueryRangeItr<'a> {
    inverted: bool,
    query: &'a str,
    content: &'a str,
}

它需要查询和内容的引用,并期望该实例与查询/内容一样长。目前为止,一切都很好。

impl<'a> QueryRangeItr<'a> {
    fn new(query: &'a str, content: &'a str, inverted: bool) -> QueryRangeItr<'a> {
        Self {
            inverted,
            query,
            content,
        }
    }
}

我有一个静态方法,允许您对每个找到的查询执行转换。它接受一个闭包并将其应用于查询,然后给您一个新的String,该字符串没有生命周期依赖关系。由于返回值没有生命周期,我实际上不想在这里为查询和内容引入生命周期,但如果我不这样做,Rust会告诉我需要...因此,它们具有生命周期要求。单独使用时,这很好。

问题出现在另一个静态方法上,它调用了两次transform来转换查询和非查询内容。首先,它调用transfrom来应用一个闭包到找到的查询。结果反馈到另一个transform方法的调用中,但这次是对其余内容应用变换。

impl<'a> QueryRangeItr<'a> {
    pub fn transform<T>(
        query: &'a str,
        content: &'a str,
        transform: T,
        inverted: bool,
    ) -> String where T: Fn(&str) -> String {
        let selects = Self::new(query, content, true);
        // ...
        // returns a `String` with no lifetime dependency on input params
    }

    pub fn transform_all<TQ, TNQ>(
        query: &'a str,
        content: &'a str,
        transform_query: TQ,
        transform_non_query: TNQ,
    ) -> String
        where
            TQ: Fn(&str) -> String,
            TNQ: Fn(&str) -> String,
    {
        let transformed_content = Self::transform(query, content, &transform_query, false);
        let transformed_query = transform_query(query);
        let transformed = Self::transform(&transformed_query, &transformed_content, transform_non_query, true); // <--- Rust expects `transformed_content` and `transformed_query` to match the lifetime of `query` and `content`
        transformed
    }

}

transformed_contenttransformed_querytransform_all结束时会被销毁...这是有意义的,但是Rust希望它们的生命周期与输入参数querycontent('a)一样长,而它们却无法做到。

实际上我并不需要让它们存在更长的时间。一旦我得到了transformed,我就不再需要它们了。然而,在querycontent上设置生命周期让Rust认为它们需要比实际需要的时间更长,导致出现了这个错误:

115 | impl<'a> QueryRangeItr<'a> {
    |      -- lifetime `'a` defined here
...
200 |         let transformed = Self::transform(&transformed_query, &transformed_content, transform_non_query, true);
    |                           ------------------------------------^^^^^^^^^^^^^^^^^^^^----------------------------
    |                           |                                   |
    |                           |                                   borrowed value does not live long enough
    |                           argument requires that `transformed_content` is borrowed for `'a`
201 |         transformed
202 |     }
    |     - `transformed_content` dropped here while still borrowed

如果我移除生命周期,将会出现以下错误:

error[E0621]: explicit lifetime required in the type of `query`
   --> src/range/query_range_iterator.rs:130:23
    |
125 |         query: &str,
    |                ---- help: add explicit lifetime `'a` to the type of `query`: `&'a str`
...
130 |         let selects = Self::new(query, content, invert);
    |                       ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ lifetime `'a` required

如果我尝试为我的转换方法定义一个不同的生命周期 'a'b'_),我会得到这样的结果:

error[E0521]: borrowed data escapes outside of associated function
   --> src/range/query_range_iterator.rs:130:23
    |
126 |         content: &'_ str,
    |         ------- `content` is a reference that is only valid in the associated function body
...
130 |         let selects = Self::new(query, content, invert);
    |                       ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `content` escapes the associated function body here

我想要做的是找到一种方法,向Rust证明只要它认为不需要transformed_contenttransformed_query,我就不需要它们。你有什么想法吗?

我在Stack Overflow上提出了一个简明扼要的问题,涉及到类似的常见rust错误error[E0716] error E0716: temporary value dropped while borrowed (rust),并且它链接回了这个问题。 - JamesThomasMoon
1个回答

7

替换

 = Self::transform(&transformed_query, &transformed_content, ...
// ^^^^

使用

 = QueryRangeItr::transform(&transformed_query, &transformed_content, ...
// ^^^^^^^^^^^^^

使用Self,你将为所有内容使用相同的生命周期'a。正如你指出的那样,transformed_contenttransformed_query仅在函数内部存在,因此它们的生命周期肯定更短,无法匹配'a。将Self替换为QueryRangeItr可以允许编译器为调用选择新的生命周期'a


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接