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() {}
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
.
2 Likes
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?
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).
2 Likes
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).
1 Like
yup, got it. Thanx man for the comprehensive reply 
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`.
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).
1 Like