提问人:some_engineer 提问时间:3/18/2023 最后编辑:some_engineer 更新时间:3/19/2023 访问量:131
不能借用“*self”。如何在没有字符串克隆的情况下使 fn 工作?
Cannot borrow `*self`. How to make the fn work without string cloning?
问:
struct Foo {
stack: Vec<String>,
}
impl Foo {
pub fn bar(&mut self) {
// find condition here is for example only.
// position in the stack is important.
if let Some(s) = self.stack.iter().find(|x| x.is_ascii()) {
self.baz(s.as_str());
}
}
fn baz(&mut self, _input: &str) {
// mutating of `self.stack` and some other fields.
}
}
error[E0502]: cannot borrow `*self` as mutable because it is also borrowed as immutable
--> src/main.rs:8:13
|
7 | if let Some(s) = self.stack.last() {
| ----------------- immutable borrow occurs here
8 | self.baz(s.as_str());
| ^^^^^---^^^^^^^^^^^^
| | |
| | immutable borrow later used by call
| mutable borrow occurs here
我不想每次都克隆一个字符串。如何让它与借来的字符串一起工作?
是的,我真的需要这里。&mut self
答:
0赞
cafce25
3/18/2023
#1
你可以用这样的方式包装你的字符串,你可以便宜地拥有一些东西,这样你就不会引用原始结构:Rc
clone
Rc
use std::rc::Rc;
struct Foo {
stack: Vec<Rc<String>>,
}
impl Foo {
pub fn bar(&mut self) {
if let Some(s) = self
.stack
.iter()
.find(|x| x.is_ascii())
.map(Rc::clone)
{
self.baz(s.as_str());
}
}
// …
}
对于基础字符串的可变访问,可以使用或进一步包装或类似。Rc::get_mut
RefCell
原始未指定问题的解决方案:
最直接的解决方案是删除调用的 from:String
Foo
baz
struct Foo {
stack: Vec,
}
impl Foo {
pub fn bar(&mut self) {
if let Some(s) = self.stack.pop() {
self.baz(s.as_str());
self.stack.push(s);
}
}
fn baz(&mut self, _input: &str) {}
}
评论
1赞
some_engineer
3/19/2023
好。但是在 x 中已经有 &Rc<_> 的类型,所以不需要,只需 .或者更简单:..map(|x| Rc::clone(&x))
&x
x
.map(Rc::clone)
0赞
1c982d
3/18/2023
#2
Cell 和 RefCell
旨在解决内部可变性问题。 通过特征来解决这个问题,而通过间接解决这个问题。通过将所有数据包装在 中,方法的参数中只需要 s,并且您仍然可以通过调用 来改变堆栈。
Cell
Copy
RefCell
RefCell
&self
borrow_mut()
RefCell
RefCell
是我首先想到的,但我无法构建一个可行的例子,所以我删除了一段时间的答案,以避免误导其他人。我的错误是坚持方法的论证,这完全浪费了力量。&mut self
RefCell
#![allow(dead_code, unused_variables, unused_imports)]
struct FooImpl {
stack: Vec<String>,
}
struct Foo {
data: std::cell::RefCell<FooImpl>,
}
impl Foo {
pub fn bar(&self) {
if let Some(s) = self.data.borrow().stack.iter().find(|x| x.is_ascii()) {
self.baz(s.as_str());
}
}
fn baz(&self, _input: &str) {
self.mutate();
}
fn mutate(&self) {
// You can mutate fields other than `stack`
}
}
避免此问题的另一种方法是使用索引而不是引用。索引(无符号整数)可以复制,因此您不会被借用检查。编译器资源管理器链接:https://godbolt.org/z/chWc5G3zK
#![allow(dead_code, unused_variables, unused_imports)]
struct Foo {
stack: Vec<String>,
}
impl Foo {
pub fn bar(&mut self) {
// `position` returns the index, while `find` returns the reference.
if let Some(index) = self.stack.iter().position(|x| x.is_ascii()) {
// ^^^
// `index` is copied, so you avoid the borrow check and make the
// program a bit more "unsafe". Since `index` is returned by
// `find`, it should be okay.
// Option 1: take the string, call `baz` and put the string back.
let value = std::mem::take(&mut self.stack[index]);
self.baz(value.as_str());
let _ = std::mem::replace(&mut self.stack[index], value);
// Option 2: create another function which takes `&mut self`
// along with an index.
self.baz_with_index(index);
}
}
fn baz(&mut self, _input: &str) {
// mutating of `self.stack` and some other fields.
}
fn baz_with_index(&mut self, index: usize) {
// mutating of `self.stack` and some other fields.
}
}
评论
Vec<Cell<String>>
.take()
Cell