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
相关产品推荐
相关产品推荐

