Cannot borrow immutable borrowed content as mutable


#1

I’m using Tokio to implement a server that write to a file. Problem with code below is that to write to a file, it has to be &mut and hence the &mut self in insert, set functions but the call method in tokio_service.Service interface requires call to have &self parameter. and that’s the error coming from running that code error[E0596]: cannot borrow immutable borrowed content as mutable

Is there a way I could do what I want around this?

extern crate tokio_service;
extern crate futures;
use std::fs::{File, OpenOptions};
use std::path::PathBuf;
use std::io::{Result, Write};
use std::os::unix::fs::OpenOptionsExt;
use std::sync::Arc;
use std::io;
use futures::{future, Future};
use tokio_service::Service;


struct Storage {
    data: Data,
}

impl Storage {
    pub fn new(dir: &PathBuf) -> Result<Self> {
        Ok(Self {
            data: Data::new(dir.join("rkv-data"))?,
        })
    }

    pub fn set(&mut self) -> Result<String> {
        self.data.insert()
    }
}

struct Data {
    handler: File,
}

impl Data {
    fn new(path: PathBuf) -> Result<Self> {
        let mut file = OpenOptions::new()
            .read(true)
            .write(true)
            .create(true)
            .mode(0o600)
            .open(path)?;
        file.write(b"X")?;
        Ok(Data { handler: file })
    }

    fn insert(&mut self) -> Result<String> {
        unimplemented!();
    }
}

struct Server {
    storage: Arc<Storage>,
}

impl<'a> Service for Server {
    type Request = String;
    type Response = String;

    type Error = io::Error;

    type Future = Box<Future<Item = Self::Response, Error = Self::Error>>;

    fn call(&self, req: Self::Request) -> Self::Future {
        let res = self.storage.set().unwrap();

        Box::new(future::ok(res))
    }
}

fn main() {}

#2

A &File also implements std::io::Write. So insert() can be something like:

fn insert(&self) -> Result<String> {
        // borrow self.handler immutably, which yields a &File, and then we can call write() on it since &File
        // implements std::io::Write
        (&self.handler).write(b"blah");
        Ok("Success".into())
}

Then you can change set to take &self rather than &mut self.


#3

yup, that solved my problem. But how does &File implements std::io::Write?. I’ve looked at the docs here I see it as a &mut. can you explain it, please?

what’s that (&self.handler) do exactly?


#4

Write::write(&mut self, ...) means Self (ie whatever type you’re implementing Write for) is borrowed mutably. Self is &File here so you end up with &mut &File. Since any immutable reference is Copy it means you can have as many &File values floating around as you want and you can borrow each mutably (since all uses of it have their own copy, so to speak).


#5

We want to get a &File type so we can borrow it mutably for the call to write (see my previous reply). Since the dot operator auto-derefs, self.handler gives us a File, not a &File. (&self.handler).write(...) forces the type to (temporarily, if you will) become &File and that’s the type we end up calling write on (which is what we want).


#6

yup, got it. Thanx man for the comprehensive reply :slight_smile:


#7

I’ve made a sample implementation to understand what you said, but it seems it’s also refuses to build and I used the same trick in storage.set. I don’t get why it worked with File but didn’t work here


use std::sync::Arc;

struct Index {
    idx: Vec<u32>,
}

impl Index {
    fn insert(&mut self) {
        self.idx.push(4);
    }
}

struct Storage {
    index: Index,
}

impl Storage {
    fn new() -> Self {
        Storage {
            index: Index { idx: vec![1, 2, 3] },
        }
    }

    fn set(&self) {
        (&self.index).insert()
    }
}

struct Server {
    storage: Arc<Storage>,
}

impl Server {
    // Can't change it
    fn call(&self) {
        self.storage.set();
    }
}

fn main() {
    let mut store = Storage::new();
    listen(Arc::new(store));
}


fn listen(storage: Arc<Storage>) {
    unimplemented!()
}

Here’s the error

warning: unused variable: `storage`
  --> src/main.rs:45:11
   |
45 | fn listen(storage: Arc<Storage>) {
   |           ^^^^^^^
   |
   = note: #[warn(unused_variables)] on by default
   = note: to avoid this warning, consider using `_storage` instead

error[E0596]: cannot borrow immutable borrowed content as mutable
  --> src/main.rs:25:9
   |
25 |         (&self.index).insert()
   |         ^^^^^^^^^^^^^ cannot borrow as mutable

warning: variable does not need to be mutable
  --> src/main.rs:40:9
   |
40 |     let mut store = Storage::new();
   |         ---^^^^^^
   |         |
   |         help: remove this `mut`
   |
   = note: #[warn(unused_mut)] on by default

error: aborting due to previous error

error: Could not compile `issues`.

#8
fn set(&self) {
  (&self.index).insert()
}

fn insert(&mut self) {
   self.idx.push(4);
}

This code requires that you have a mutable borrow of self (i.e. Index) to call insert. But set only has an immutable borrow of Storage, and since Storage owns the Index, it has only an immutable borrow of Index as well.

To make your code work, you’ll need to introduce interior mutability somewhere. Here’s one possibility (note the Server::call change):

use std::sync::Arc;
use std::cell::RefCell;

struct Index {
    idx: Vec<u32>,
}

impl Index {
    fn insert(&mut self) {
        self.idx.push(4);
    }
}

struct Storage {
    index: Index,
}

impl Storage {
    fn new() -> Self {
        Storage {
            index: Index { idx: vec![1, 2, 3] },
        }
    }

    fn set(&mut self) {
        self.index.insert()
    }
}

struct Server {
    storage: Arc<RefCell<Storage>>,
}

impl Server {
    fn call(&self) {
        // interior mutability here
        self.storage.borrow_mut().set();
    }
}

fn main() {
    let mut store = Storage::new();
    listen(Arc::new(RefCell::new(store)));
}


fn listen(storage: Arc<RefCell<Storage>>) {
    unimplemented!()
}

Most types in Rust will require a &mut to perform mutation, such as this case where you have a Vec<u32> at the “bottom” of the call chain - it does not allow mutation without having a mutable reference to it.

File is special in that there’s an implementation of mutable APIs for &File. This is allowed because the underlying OS file operations already provide the required guarantees that allow writes to it across aliasing references. The analogous here would be if Vec::push was defined as pub fn push(&self, item: T) ..., but that doesn’t exist because a Vec does not support aliasing mutation (i.e. mutation from multiple shared references).