Cannot borrow as mutable because it is also borrowed as immutable, but it doesn't

Greetings!

What I want to achieve:

  1. store old keys hashes;
  2. change object;
  3. gen new keys hashes;

gen_key_hashes generate array with integers, so what kind of borrow are we talking about?

error[E0502]: cannot borrow `*foo` as mutable because it is also borrowed as immutable
  --> src/main.rs:44:5
   |
43 |     let old_key_hashes = foo.gen_key_hashes();
   |                          -------------------- immutable borrow occurs here
44 |     update_value(foo);
   |     ^^^^^^^^^^^^^^^^^ mutable borrow occurs here
...
47 |     println!("before: {:?}", old_key_hashes);
   |                              -------------- immutable borrow later used here
use smallvec::{smallvec, Array, SmallVec};
use std::{
    collections::hash_map::DefaultHasher,
    fmt::Debug,
    hash::{Hash, Hasher},
};

trait Foo<'a> {
    type Key: Debug + Hash + 'a;
    type KeysArray: Array<Item = Self::Key>;
    type KeyHashesArray: Array<Item = u64>;

    fn gen_keys(&'a self) -> SmallVec<Self::KeysArray>;

    fn gen_key_hashes(&'a self) -> SmallVec<Self::KeyHashesArray> {
        self.gen_keys().iter().map(calculate_hash).collect()
    }
}

#[derive(Debug, Hash)]
enum BarKey<'a> {
    Key(&'a str),
}

struct Bar {
    key: String,
}
impl<'a> Foo<'a> for Bar {
    type Key = BarKey<'a>;
    type KeysArray = [Self::Key; 10];
    type KeyHashesArray = [u64; 10];

    fn gen_keys(&'a self) -> SmallVec<Self::KeysArray> {
        smallvec![BarKey::Key(&self.key)]
    }
}

fn test<V, ValueUpdate>(foo: &mut V, update_value: ValueUpdate)
where
    V: for<'any> Foo<'any>,
    ValueUpdate: FnOnce(&mut V),
{
    let old_key_hashes = foo.gen_key_hashes();
    update_value(foo);
    let new_key_hashes = foo.gen_key_hashes();

    println!("before: {:?}", old_key_hashes);
    println!("after: {:?}", new_key_hashes);
}

fn main() {
    let mut bar = Bar {
        key: "key".to_owned(),
    };

    test(&mut bar, |value| {
        value.key = "modified".to_owned();
    });
}

pub fn calculate_hash<T: Hash>(t: &T) -> u64 {
    let mut s = DefaultHasher::new();
    t.hash(&mut s);
    s.finish()
}

It looks like you're trying to make Self::Key and Self::KeysArray match the lifetime of the &self borrow, which isn't possible in stable Rust (at least, not without much finagling with helper traits). In nightly Rust, this is possible with the generic_associated_types feature, which will likely become stable by the end of June (Rust Playground):

#![feature(generic_associated_types)]

use smallvec::{smallvec, Array, SmallVec};
use std::{
    collections::hash_map::DefaultHasher,
    fmt::Debug,
    hash::{Hash, Hasher},
};

trait Foo {
    type Key<'a>: Debug + Hash
    where
        Self: 'a;

    type KeysArray<'a>: Array<Item = Self::Key<'a>>
    where
        Self: 'a;

    type KeyHashesArray: Array<Item = u64>;

    fn gen_keys(&self) -> SmallVec<Self::KeysArray<'_>>;

    fn gen_key_hashes(&self) -> SmallVec<Self::KeyHashesArray> {
        self.gen_keys().iter().map(calculate_hash).collect()
    }
}

#[derive(Debug, Hash)]
enum BarKey<'a> {
    Key(&'a str),
}

struct Bar {
    key: String,
}

impl Foo for Bar {
    type Key<'a> = BarKey<'a>;
    type KeysArray<'a> = [Self::Key<'a>; 10];
    type KeyHashesArray = [u64; 10];

    fn gen_keys(&self) -> SmallVec<Self::KeysArray<'_>> {
        smallvec![BarKey::Key(&self.key)]
    }
}

fn test<V, ValueUpdate>(foo: &mut V, update_value: ValueUpdate)
where
    V: Foo,
    ValueUpdate: FnOnce(&mut V),
{
    let old_key_hashes = foo.gen_key_hashes();
    update_value(foo);
    let new_key_hashes = foo.gen_key_hashes();

    println!("before: {:?}", old_key_hashes);
    println!("after: {:?}", new_key_hashes);
}

fn main() {
    let mut bar = Bar {
        key: "key".to_owned(),
    };

    test(&mut bar, |value| {
        value.key = "modified".to_owned();
    });
}

pub fn calculate_hash<T: Hash>(t: &T) -> u64 {
    let mut s = DefaultHasher::new();
    t.hash(&mut s);
    s.finish()
}

Yeah, I'm aware of generic_associated_types, figuring out how to get around nightly features in stable is my personal hell....

Copy-paste gen_key_hashes function solved the error, but it copy-paste =(

Here you go.

And a step-by-step for emulating lifetime GATs.

2 Likes

With _Outlives: ?Sized = &'a Self it gives is not general enough error. Without it, work fine. And I can't move KeyHashesArray into Gat trait, but still, something at least.
I already used idea with GAT emulation, but in that case I can't figure how to do it, your help was maximally useful =)

Thanks a lot =)