Rust借用检查器误报多次可变借用的原因排查及无unsafe代码修复方案咨询
FilterTextStream Let’s break down what’s going on here and how to fix this without relying on unsafe code.
Why the Error Happens
You’re absolutely right about the logic: your code only takes one path at a time—either returns a valid &str or recurses to try again. The problem is that Rust’s borrow checker can’t yet track this kind of mutually exclusive branch logic when recursion is involved.
When you return Some(txt), the checker ties the lifetime of that &str to the mutable borrow of self (the &mut self parameter with lifetime '1). Then, when it sees the recursive call self.next_str() in the other branch, it interprets this as a second mutable borrow of self that overlaps with the first—even though in practice, those branches never run together. The checker doesn’t have the context to know that returning Some(txt) exits the function immediately, so it flags the recursive call as a conflicting borrow.
Your unsafe workaround works because it forcefully decouples the returned &str’s lifetime from self, but this is risky: it bypasses Rust’s safety guarantees. If the TextStream contract ever changes (e.g., a future implementation returns a &str tied to the stream’s internal state), you could end up with dangling references.
A Safe Fix: Replace Recursion with a Loop
The simplest, most reliable fix is to swap the recursive call for an iterative loop. This makes the control flow explicit enough for the borrow checker to validate safely. Here’s the rewritten implementation:
impl<T: TextStream> TextStream for FilterTextStream<T> { fn next_str(&mut self) -> Option<&str> { loop { match self.inner.next_str() { None => return None, Some(txt) if txt.len() <= self.maxlen => return Some(txt), // Skip overlong strings and try again _ => continue, } } } }
Why This Works
- Each loop iteration starts with a fresh call to
self.inner.next_str(), which borrowsselfmutably only for the duration of that call. - When we hit
return, we exit the function immediately, so there’s no chance of overlapping borrows with subsequent iterations. - The borrow checker can clearly see that every path either releases the borrow (via return) or starts fresh with a new borrow (via loop continuation).
Final Takeaway
Your initial intuition was spot-on: this is a limitation of Rust’s borrow checker, not a flaw in your logic. Recursive mutable borrows are a tricky case for the checker, as it can’t track that the recursive call only runs if the return path isn’t taken. By switching to an iterative loop, you eliminate the confusing recursive borrow and keep your code safe, no unsafe hacks required.
内容的提问来源于stack exchange,提问作者Pierre-Antoine




