1

I'm trying to wrap a slice in a struct so that I will be able to instantiate the struct mutably or immutably. Here's a minimal example:

use std::ops::{ Index, IndexMut }; struct Test<'a, T: 'a> { inner: &'a[T] } impl<'a, T: 'a> Test<'a, T> { fn new (inner: &'a[T]) -> Self { Test { inner: inner } } } impl<'a, T> Index<usize> for Test<'a, T> { type Output = T; fn index (&self, i: usize) -> &T { &self.inner[i] } } impl<'a, T> IndexMut<usize> for Test<'a, T> { fn index_mut (&mut self, i: usize) -> &mut T { &mut self.inner[i] } } fn main() { let store = [0; 3]; let test = Test::new (&store); println!("{}", test[1]); let mut mut_store = [0; 3]; let mut mut_test = Test::new (&mut mut_store); mut_test[1] = 42; println!("{}", mut_test[1]); } 

This doesn't compile: "cannot borrow immutable indexed content self.inner[..] as mutable".

I could get it to compile by changing the definition of inner to be of type &'a mut[T], but then inner is mutable even when I don't need it to be (in the above example, I must then declare store as mutable too even though test is immutable).

Is there a way to make it so that the mutability of inner follows the mutability of the Test instance?

1 Answer 1

4

As well said in the question, this code compiles:

struct Test<'a, A: 'a> { inner: &'a mut A, } fn main() { let t = Test { inner: &mut 5i32 }; *t.inner = 9; } 

It is indeed possible to mutate a borrowed element, even when the borrowing content is immutable. This is a case where you must choose your guarantees, while keeping in mind that the mutability of a binding is always independent of the borrowed content's mutability.

Right now, I can think of two possible solutions: you can encapsulate the borrowed content over methods that depend on self's mutability (Playground, will no longer compile):

impl<'a, A: 'a> Test<'a, A> { fn inner(&self) -> &A { self.inner } fn inner_mut(&mut self) -> &mut A { self.inner } } 

Although you still need to keep a borrow to mutable content, it can no longer be mutated from an immutable binding of Test. If you also need it to point to immutable content, you should consider having two different structs (Playground):

struct Test<'a, A: 'a> { inner: &'a A, } impl<'a, A: 'a> Test<'a, A> { fn inner(&self) -> &A { self.inner } } struct TestMut<'a, A: 'a> { inner: &'a mut A, } impl<'a, A: 'a> TestMut<'a, A> { fn inner(&self) -> &A { self.inner } fn inner_mut(&mut self) -> &mut A { self.inner } } 

There is a third option: to keep both kinds of borrows exclusively with an enum. At this point however, using the borrowed content as mutable requires run-time checks.

Sign up to request clarification or add additional context in comments.

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.