Reputation: 1874
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
I don't want to clone a string every time. How to get it working with borrowed string?
And yes, I really need the &mut self
here.
Upvotes: -1
Views: 158
Reputation: 474
Cell
and RefCell
are designed to solve interior mutability problems. Cell
sloves this by Copy
trait, while RefCell
solves this by an indirection. By wrapping all the data inside a RefCell
, only &self
s are needed in methods' arguments, and you can still mutate the stack by calling borrow_mut()
on RefCell
.
RefCell
was the first thing I thought of, but I wasn't able to construct a viable example, so I deleted the answer for some time to avoid misleading others. My mistake was to keep &mut self
in methods' arguments, which totally wasted RefCell
's power.
#![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`
}
}
Another way to avoid this problem is to use index instead of reference. Indices (which are unsigned integers) can be copied, so you don't get borrow checked. Compiler explorer link: 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.
}
}
Upvotes: 0
Reputation: 27218
You can wrap your strings in Rc
that way you can cheaply clone
the Rc
and have something owned so you don't reference the original struct:
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());
}
}
// …
}
For mutable access to the underlying strings you can use Rc::get_mut
or wrap further in RefCell
or similar.
Solution to the original underspecified question:
The most straight forward solution is to just remove the String
from Foo
for the baz
call:
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) {}
}
Upvotes: 0