我们如何解决有条件地返回字符串切片时发生的生命周期错误?
How can we solve this lifetime error occuring when conditionally returning a string slice?
期望的行为是,如果你可以切断 4 个字节和 return 4 个字节,否则如果你可以切断 2 个字节和 return 2 个字节,否则 return一个错误。
方法cut
、cut_alternative
和cut_alternative_1
试图实现这一点。下面列出了编译器错误消息。
为什么所有这些示例方法都无法编译?在cut_alternative_1
中,借用彼此分开,而错误消息是相同的。
你能给出解决方法吗?
struct StrWrapper<'s> {
content: &'s str,
}
impl<'s> StrWrapper<'s> {
fn cut(&mut self) -> Result<&str, ()> {
self.cut4()
.or(self.cut2())
}
fn cut_alternative(&mut self) -> Result<&str, ()> {
if let Ok(part) = self.cut4() {
Ok(part)
} else if let Ok(part) = self.cut2() {
Ok(part)
} else {
Err(())
}
}
fn cut_alternative_1(&'s mut self) -> Result<&'s str, ()> {
{
let part = self.cut4();
if part.is_ok() {
return part;
}
}
{
let part = self.cut2();
if part.is_ok() {
return part;
}
}
Err(())
}
fn cut2(&mut self) -> Result<&str, ()> {
if self.content.len() >= 2 {
let part = &self.content[..2];
self.content = &self.content[2..];
Ok(part)
} else {
Err(())
}
}
fn cut4(&mut self) -> Result<&str, ()> {
if self.content.len() >= 4 {
let part = &self.content[..4];
self.content = &self.content[4..];
Ok(part)
} else {
Err(())
}
}
}
error[E0499]: cannot borrow `*self` as mutable more than once at a time
--> src/parser.rs:210:17
|
208 | fn cut(&mut self) -> Result<&str, ()> {
| - let's call the lifetime of this reference `'1`
209 | self.cut4()
| ----
| |
| _________first mutable borrow occurs here
| |
210 | | .or(self.cut2())
| |_________________^^^^_______- returning this value requires that `*self` is borrowed for `'1`
| |
| second mutable borrow occurs here
error[E0499]: cannot borrow `*self` as mutable more than once at a time
--> src/parser.rs:216:34
|
213 | fn cut_alternative(&mut self) -> Result<&str, ()> {
| - let's call the lifetime of this reference `'1`
214 | if let Ok(part) = self.cut4() {
| ---- first mutable borrow occurs here
215 | Ok(part)
| -------- returning this value requires that `*self` is borrowed for `'1`
216 | } else if let Ok(part) = self.cut2() {
| ^^^^ second mutable borrow occurs here
error[E0499]: cannot borrow `*self` as mutable more than once at a time
--> src/parser.rs:230:24
|
207 | impl<'s> StrWrapper<'s> {
| -- lifetime `'s` defined here
...
224 | let part = self.cut4();
| ---- first mutable borrow occurs here
225 | if part.is_ok() {
226 | return part;
| ---- returning this value requires that `*self` is borrowed for `'s`
...
230 | let part = self.cut2();
| ^^^^ second mutable borrow occurs here
@Shepmaster 对类似问题评论了 link:
这个问题有两点不同:
- 有多个可变借用,而不是一个不可变借用和一个可变借用。
- 我现在无法应用解决此问题的方法。非常感谢解决方法。
HashMap
中的秘诀是 contains_key()
的使用,return 是一个不带终生包袱的布尔值。同样,Vec
解决方案涉及在关键位置使用索引而不是切片。
在您的情况下,等效改编需要将签名 cut4()
和 cut2()
更改为 return Result<Range<usize>, ()>
。然后你就可以这样写了:
fn cut(&mut self) -> Result<&str, ()> {
if let Ok(part) = self.cut4() {
Ok(&self.content[part])
} else if let Ok(part) = self.cut2() {
Ok(&self.content[part])
} else {
Err(())
}
}
fn cut2(&mut self) -> Result<Range<usize>, ()> {
if self.content.len() >= 2 {
let part = 0..2;
self.content = &self.content[2..];
Ok(part)
} else {
Err(())
}
}
// likewise for cut4
另一种选择是使用内部可变性并使所有函数都接受 &self
:
use std::cell::Cell;
struct StrWrapper<'s> {
content: Cell<&'s str>,
}
impl<'s> StrWrapper<'s> {
fn cut(&self) -> Result<&str, ()> {
if let Ok(part) = self.cut4() {
Ok(part)
} else if let Ok(part) = self.cut2() {
Ok(part)
} else {
Err(())
}
}
fn cut2(&self) -> Result<&str, ()> {
if self.content.get().len() >= 2 {
let part = &self.content.get()[..2];
self.content.set(&self.content.get()[2..]);
Ok(part)
} else {
Err(())
}
}
// likewise for cut4
}
Playground(我真的很惊讶这个编译,我以前从未尝试过在 Cell
中放置引用。)
最后,如果以上两种选择都不适合你,那总有一种不安全:
fn cut(&mut self) -> Result<&str, ()> {
// Safety: slices returned by cut4() and cut2() must live at least as
// long as `self` (because the lifetime of `&str` returned is tied to the
// lifetime of `&self`). This is the case because cut2() and cut4() are
// bound by their signatures to return sub-slices of `self`, and we don't
// modify `self` between a successful call to cut*() and the return.
unsafe {
if let Ok((ptr, len)) = self.cut4().map(|s| (s.as_ptr(), s.len())) {
Ok(std::str::from_utf8(std::slice::from_raw_parts(ptr, len)).unwrap())
} else if let Ok((ptr, len)) = self.cut2().map(|s| (s.as_ptr(), s.len())) {
Ok(std::str::from_utf8(std::slice::from_raw_parts(ptr, len)).unwrap())
} else {
Err(())
}
}
}
如果 unsafe
巫术看起来很可怕,那很好 - 它应该。如果你犯了错误,编译器不会帮助你,所以这种事情只能作为最后的手段使用。我 认为 上面的代码是正确的,因为它基本上与范围版本的工作方式相同,它只是在创建切片后将其转换为它们的组成部分。但是,任何时候有人重构代码时,您都会想知道是否崩溃,或者更糟的是,无声的 UB 正潜伏在拐角处。
如果还不是很明显,我的建议是方法 #1。
期望的行为是,如果你可以切断 4 个字节和 return 4 个字节,否则如果你可以切断 2 个字节和 return 2 个字节,否则 return一个错误。
方法cut
、cut_alternative
和cut_alternative_1
试图实现这一点。下面列出了编译器错误消息。
为什么所有这些示例方法都无法编译?在cut_alternative_1
中,借用彼此分开,而错误消息是相同的。
你能给出解决方法吗?
struct StrWrapper<'s> {
content: &'s str,
}
impl<'s> StrWrapper<'s> {
fn cut(&mut self) -> Result<&str, ()> {
self.cut4()
.or(self.cut2())
}
fn cut_alternative(&mut self) -> Result<&str, ()> {
if let Ok(part) = self.cut4() {
Ok(part)
} else if let Ok(part) = self.cut2() {
Ok(part)
} else {
Err(())
}
}
fn cut_alternative_1(&'s mut self) -> Result<&'s str, ()> {
{
let part = self.cut4();
if part.is_ok() {
return part;
}
}
{
let part = self.cut2();
if part.is_ok() {
return part;
}
}
Err(())
}
fn cut2(&mut self) -> Result<&str, ()> {
if self.content.len() >= 2 {
let part = &self.content[..2];
self.content = &self.content[2..];
Ok(part)
} else {
Err(())
}
}
fn cut4(&mut self) -> Result<&str, ()> {
if self.content.len() >= 4 {
let part = &self.content[..4];
self.content = &self.content[4..];
Ok(part)
} else {
Err(())
}
}
}
error[E0499]: cannot borrow `*self` as mutable more than once at a time
--> src/parser.rs:210:17
|
208 | fn cut(&mut self) -> Result<&str, ()> {
| - let's call the lifetime of this reference `'1`
209 | self.cut4()
| ----
| |
| _________first mutable borrow occurs here
| |
210 | | .or(self.cut2())
| |_________________^^^^_______- returning this value requires that `*self` is borrowed for `'1`
| |
| second mutable borrow occurs here
error[E0499]: cannot borrow `*self` as mutable more than once at a time
--> src/parser.rs:216:34
|
213 | fn cut_alternative(&mut self) -> Result<&str, ()> {
| - let's call the lifetime of this reference `'1`
214 | if let Ok(part) = self.cut4() {
| ---- first mutable borrow occurs here
215 | Ok(part)
| -------- returning this value requires that `*self` is borrowed for `'1`
216 | } else if let Ok(part) = self.cut2() {
| ^^^^ second mutable borrow occurs here
error[E0499]: cannot borrow `*self` as mutable more than once at a time
--> src/parser.rs:230:24
|
207 | impl<'s> StrWrapper<'s> {
| -- lifetime `'s` defined here
...
224 | let part = self.cut4();
| ---- first mutable borrow occurs here
225 | if part.is_ok() {
226 | return part;
| ---- returning this value requires that `*self` is borrowed for `'s`
...
230 | let part = self.cut2();
| ^^^^ second mutable borrow occurs here
@Shepmaster 对类似问题评论了 link:
这个问题有两点不同:
- 有多个可变借用,而不是一个不可变借用和一个可变借用。
- 我现在无法应用解决此问题的方法。非常感谢解决方法。
HashMap
contains_key()
的使用,return 是一个不带终生包袱的布尔值。同样,Vec
解决方案涉及在关键位置使用索引而不是切片。
在您的情况下,等效改编需要将签名 cut4()
和 cut2()
更改为 return Result<Range<usize>, ()>
。然后你就可以这样写了:
fn cut(&mut self) -> Result<&str, ()> {
if let Ok(part) = self.cut4() {
Ok(&self.content[part])
} else if let Ok(part) = self.cut2() {
Ok(&self.content[part])
} else {
Err(())
}
}
fn cut2(&mut self) -> Result<Range<usize>, ()> {
if self.content.len() >= 2 {
let part = 0..2;
self.content = &self.content[2..];
Ok(part)
} else {
Err(())
}
}
// likewise for cut4
另一种选择是使用内部可变性并使所有函数都接受 &self
:
use std::cell::Cell;
struct StrWrapper<'s> {
content: Cell<&'s str>,
}
impl<'s> StrWrapper<'s> {
fn cut(&self) -> Result<&str, ()> {
if let Ok(part) = self.cut4() {
Ok(part)
} else if let Ok(part) = self.cut2() {
Ok(part)
} else {
Err(())
}
}
fn cut2(&self) -> Result<&str, ()> {
if self.content.get().len() >= 2 {
let part = &self.content.get()[..2];
self.content.set(&self.content.get()[2..]);
Ok(part)
} else {
Err(())
}
}
// likewise for cut4
}
Playground(我真的很惊讶这个编译,我以前从未尝试过在 Cell
中放置引用。)
最后,如果以上两种选择都不适合你,那总有一种不安全:
fn cut(&mut self) -> Result<&str, ()> {
// Safety: slices returned by cut4() and cut2() must live at least as
// long as `self` (because the lifetime of `&str` returned is tied to the
// lifetime of `&self`). This is the case because cut2() and cut4() are
// bound by their signatures to return sub-slices of `self`, and we don't
// modify `self` between a successful call to cut*() and the return.
unsafe {
if let Ok((ptr, len)) = self.cut4().map(|s| (s.as_ptr(), s.len())) {
Ok(std::str::from_utf8(std::slice::from_raw_parts(ptr, len)).unwrap())
} else if let Ok((ptr, len)) = self.cut2().map(|s| (s.as_ptr(), s.len())) {
Ok(std::str::from_utf8(std::slice::from_raw_parts(ptr, len)).unwrap())
} else {
Err(())
}
}
}
如果 unsafe
巫术看起来很可怕,那很好 - 它应该。如果你犯了错误,编译器不会帮助你,所以这种事情只能作为最后的手段使用。我 认为 上面的代码是正确的,因为它基本上与范围版本的工作方式相同,它只是在创建切片后将其转换为它们的组成部分。但是,任何时候有人重构代码时,您都会想知道是否崩溃,或者更糟的是,无声的 UB 正潜伏在拐角处。
如果还不是很明显,我的建议是方法 #1。