2017-08-24 6 views
2

Est-il possible d'emprunter des parties d'une struct mutable, et une autre partie comme immuable - si les champs de la struct sont privées.Est-il possible d'emprunter des parties d'une structure comme mutable et d'autres parties comme immuables?

fn main() { 
    let mut ecs = EntityComponentSystem::new(); 

    for e_id in ecs.get_entities_with_component::<Velocity>().unwrap() { 
      let components = ecs.get_mut_components(e_id); 
      ... 
} 

impl EntityComponentSystem { 
    ... 
    pub fn get_entities_with_component<K: Key>(&self) -> Option<&HashSet<u64>> { 
     self.entities_with_components.get(&TypeId::of::<K>()) 
    } 

    pub fn get_mut_components(&mut self, entity_id: u64) -> &mut TypeMap { 
     let entity = self.entities.get_mut(&entity_id).unwrap(); 
     &mut entity.components 
    } 
} 

pub struct EntityComponentSystem { 
    entities: HashMap<u64, Entity>,      <------- I would like to modify this. 
    entities_with_components: HashMap<TypeId, HashSet<u64>>, <---- while reading from this! 
} 

Le compilateur me donne:

error[E0502]: cannot borrow `*ecs` as mutable because it is also borrowed as immutable 
    --> physics.rs:19:26 
    | 
18 |  for e_id in ecs.get_entities_with_component::<Velocity>() { 
    |     --- immutable borrow occurs here 
19 |   let components = ecs.get_mut_components(*e_id); 
    |       ^^^ mutable borrow occurs here 
... 
26 |  } 
    |  - immutable borrow ends here 

Qu'est-ce que je ne suis pas saisir, comment le rapport &self dans get_entities_with_component est toujours emprunté après que nous avons essentiellement a renvoyé une partie du champ entities_with_components.

ne doit pas seulement être cette partie empruntée? Y a-t-il un moyen de faire respecter cela?

Répondre

1

Vous ne pouvez emprunter la struct toute comme immuable ou mutable, il n'y a pas de notion de pièces que l'emprunt de celui-ci. Lorsque cela devient un problème, vous pouvez utiliser interior mutability sous la forme d'un RefCelll:

pub struct EntityComponentSystem { 
    entities: RefCell<HashMap<u64, Entity>>, 
    entities_with_components: HashMap<TypeId, HashSet<u64>>, 
} 

Maintenant, vous pouvez emprunter l'ensemble struct comme immuable et emprunter le contenu du RefCell indépendamment mutable:

pub fn get_mut_components(&self, entity_id: u64) -> &mut TypeMap { 
    let mut entities = self.entities.borrow_mut(); 
    let entity = entities.get_mut(&entity_id).unwrap(); 
    &mut entity.components 
} 
+0

"Vous ne pouvez emprunter que la structure entière comme immuable ou mutable, il n'y a pas de concept d'en emprunter seulement des parties." Pour être pédant, oui il y a. Vous pouvez faire un emprunt partiel sur une structure, mais cela ne fonctionnera pas pour l'op parce que les champs sont privés. Voici un exemple: https://play.rust-lang.org/?gist=fcfcca700ba238287063143754e15c97&version=stable – Timidger

+0

@Timidger TIL, merci :) –

0

Non, une fonction ne peut pas renvoyer une référence à une partie de la structure et de laisser la structure partiellement empruntée.

Cependant, vous pouvez revenir tuple de Borrows immuables et mutables comme celui-ci

#[derive(Debug)] 
struct AB(u32, u32); 

impl AB { 
    fn borrow_parts(&mut self) -> (&u32, &mut u32) { 
     (&self.0, &mut self.1) 
    } 
} 

fn main() { 
    let mut ab = AB(0, 2); 
    { 
     let (a, b) = ab.borrow_parts(); 
     *b = *a; 
    } 
    println!("{:?}", ab); 
} 
+0

Je ne sais pas comment vous feriez cela dans le cas d'OP, parce qu'ils font l'emprunt immuable une fois en dehors d'une boucle, et l'emprunt mutable emprunte plusieurs fois dans la boucle. – interjay

+0

@interjay, plusieurs Borrows mutables ne sont pas tenus de le faire si 'get_mut_components' seraient mis en œuvre pour' HashMap > 'ou un certain type d'emballage, référence mutable auquel sera retourné comme une partie d'un résultat de' emprunter_parts'. – red75prime