You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Rust返回引用当前函数数据错误排查:字符串借用迭代器场景

修复Rust字符串分割迭代器的生命周期错误

我实现了一个按谓词分割字符串的迭代器,运行时遇到了生命周期错误。

代码实现

use std::{fmt, iter::FusedIterator};

pub trait StrChunkBy {
    fn chunk_by<F>(&self, pred: F) -> ChunkBy<'_, F>
    where
        F: FnMut(char, char) -> bool;
}

impl StrChunkBy for &str {
    #[inline]
    fn chunk_by<F>(&self, pred: F) -> ChunkBy<'_, F>
    where
        F: FnMut(char, char) -> bool,
    {
        ChunkBy::new(self, pred)
    }
}

/// An iterator over str chunks separated by a predicate.
///
/// This struct is created by the [`chunk_by`] method on [str].
///
/// [`chunk_by`]: str::chunk_by
#[must_use = "iterators are lazy and do nothing unless consumed"]
pub struct ChunkBy<'a, P> {
    string: &'a str,
    predicate: P,
}

impl<'a, P> ChunkBy<'a, P> {
    pub fn new(string: &'a str, predicate: P) -> Self {
        ChunkBy { string, predicate }
    }
}

impl<'a, P> Iterator for ChunkBy<'a, P>
where
    P: FnMut(char, char) -> bool,
{
    type Item = &'a str;

    #[inline]
    fn next(&mut self) -> Option<Self::Item> {
        if self.string.is_empty() {
            None
        } else {
            let mut len = None;
            let mut left_char = None;
            for (right_index, right_char) in self.string.char_indices() {
                if let Some(left_char) = left_char {
                    if !(self.predicate)(left_char, right_char) {
                        len = Some(right_index);
                        break;
                    }
                }
                left_char = Some(right_char);
            }
            let (head, tail) = self.string.split_at(len.unwrap_or(self.string.len()));
            self.string = tail;
            Some(head)
        }
    }

    #[inline]
    fn size_hint(&self) -> (usize, Option<usize>) {
        if self.string.is_empty() {
            (0, Some(0))
        } else {
            (1, Some(self.string.len()))
        }
    }
}

impl<'a, P> FusedIterator for ChunkBy<'a, P> where P: FnMut(char, char) -> bool {}

impl<'a, P> fmt::Debug for ChunkBy<'a, P> {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        f.debug_struct("ChunkBy")
            .field("string", &self.string)
            .finish()
    }
}

// ----------

fn split_numbers<'a>(x: &'a str) -> Vec<&'a str> {
    let mut parts = Vec::new();
    for text in x
        .chunk_by(|c0, c1| c0.is_ascii_alphanumeric() == c1.is_ascii_alphanumeric())
    {
        parts.push(text);
    }
    parts
}

错误信息

error[E0515]: cannot return value referencing function parameter `x`
  --> src/main.rs:93:5
   |
88 |     for text in x
   |                 - `x` is borrowed here
...
93 |     parts
   |     ^^^^^ returns a value referencing data owned by the current function

我推测错误原因是:text 借用了 ChunkBy::string,尽管该字段引用的原字符串生命周期足够,但 ChunkBy 实例会被销毁。请问该如何修复这个问题?


解决方案

问题核心是生命周期绑定错误:你为&str实现了StrChunkBy trait,当调用x.chunk_by(...)时,x本身是&'a str,此时&self的类型是&&'a str(对字符串引用的二次引用)。直接把self传给ChunkBy::new会导致ChunkBy的string字段绑定到这个二次引用的临时生命周期,而非原字符串的'a生命周期,最终迭代器产出的&str会被判定为引用了临时值。

有两种修复方式:

方式一:为str原始类型实现trait

这是更符合Rust惯例的写法,直接给str类型实现trait,这样&self就是&'a str,能直接传递原字符串的生命周期:

impl StrChunkBy for str {
    #[inline]
    fn chunk_by<F>(&self, pred: F) -> ChunkBy<'_, F>
    where
        F: FnMut(char, char) -> bool,
    {
        ChunkBy::new(self, pred)
    }
}

这种写法支持&str、String(自动解引用)等多种字符串类型调用,ChunkBy的string字段会正确绑定到原字符串的'a生命周期,迭代器产出的&'a str也能和输入字符串的生命周期保持一致。

方式二:在trait实现中解引用self

如果坚持为&str实现trait,需要在方法内部解引用二次引用,获取原始的&'a str:

impl StrChunkBy for &str {
    #[inline]
    fn chunk_by<F>(&self, pred: F) -> ChunkBy<'_, F>
    where
        F: FnMut(char, char) -> bool,
    {
        ChunkBy::new(*self, pred)
    }
}

*self将&&'a str解引用为&'a str,让ChunkBy的string字段绑定到原字符串的生命周期,避免临时引用问题。

两种方法都能解决错误,推荐第一种写法,它的适用性更广,更符合Rust的设计习惯。


内容的提问来源于stack exchange,提问作者Timmmm

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.15 19:42:01