2016-10-17 15 views
6

Mam strukturę, która wygląda nieco jak poniżej:Jak przywrócić odwołanie do wartości podrzędnej wartości pod muteksem?

pub struct MyStruct { 
    data: Arc<Mutex<HashMap<i32, Vec<i32>>>>, 
} 

mogę łatwo dostać blokadę na mutex i kwerendy bazowego HashMap:

let d = s.data.lock().unwrap(); 
let v = d.get(&1).unwrap(); 
println!("{:?}", v); 

Teraz chcę uczynić metodę incapsulate Zapytanie, więc piszę coś takiego:

impl MyStruct { 
    pub fn get_data_for(&self, i: &i32) -> &Vec<i32> { 
     let d = self.data.lock().unwrap(); 
     d.get(i).unwrap() 
    } 
} 

To oczywiście nie kompiluje, ponieważ próbuję r eturn odniesienie do danych pod mutex:

error: `d` does not live long enough 
    --> <anon>:30:9 
    | 
30 |   d.get(i).unwrap() 
    |  ^
    | 
note: reference must be valid for the anonymous lifetime #1 defined on the block at 28:53... 
    --> <anon>:28:54 
    | 
28 |  pub fn get_data_for(&self, i: &i32) -> &Vec<i32> { 
    |             ^
note: ...but borrowed value is only valid for the block suffix following statement 0 at 29:42 
    --> <anon>:29:43 
    | 
29 |   let d = self.data.lock().unwrap(); 
    |           ^

mogę to naprawić poprzez owinięcie HashMap wartości do Arc, ale wygląda brzydki (Arc w Arc) i komplikuje kod:

pub struct MyStruct { 
    data: Arc<Mutex<HashMap<i32, Arc<Vec<i32>>>>>, 
} 

Jaki jest najlepszy sposób podejścia do tego? Czy jest możliwe stworzenie metody, która robi to, co chcę, bez modyfikowania struktury danych?

Full example code.

+3

Ładne pytanie. Spodziewałem się metody 'map()' na 'MutexGuard', jak [' Ref :: map() '] (https://doc.rust-lang.org/std/cell/struct.Ref.html#method .map) ... Dlaczego go nie ma? \ * \ _ \ * –

+0

'' ' impl MyStruct { fn with_data (f: F) {...}} ? ' '' Czy to praca w zasadzie tylko pozwolić użytkownikowi zapewnić funkcję, która zmodyfikuje te dane, gdy są zablokowane, zamiast próbować je zwrócić? –

+0

@ dpc.pw Tak. Myślałem również o podejściu do zamknięcia ... Nie bardzo piękne, ale robi to, czego potrzebuję ... Najlepsze rozwiązanie byłoby coś w rodzaju 'map()' jak wspomniał Lukas, lub jakiejś dziwacznej magii manipulowania życiem, ale nie mogę wymyślić żadnej ... –

Odpowiedz

3

Rozwiązanie to jest podobne do @ Neikos, ale stosując owning_ref zrobić przytrzymaj MutexGuard i odniesienie do Vec:

extern crate owning_ref; 
use std::sync::Arc; 
use std::sync::{Mutex,MutexGuard}; 
use std::collections::HashMap; 
use std::vec::Vec; 
use owning_ref::MutexGuardRef; 

type HM = HashMap<i32, Vec<i32>>; 

pub struct MyStruct { 
    data: Arc<Mutex<HM>>, 
} 

impl MyStruct { 
    pub fn new() -> MyStruct { 
     let mut hm = HashMap::new(); 
     hm.insert(3, vec![2,3,5,7]); 
     MyStruct{ 
      data: Arc::new(Mutex::new(hm)), 
     } 
    } 
    pub fn get_data_for<'ret, 'me:'ret, 'c>(&'me self, i: &'c i32) -> MutexGuardRef<'ret, HM, Vec<i32>> { 
     MutexGuardRef::new(self.data.lock().unwrap()) 
       .map(|mg| mg.get(i).unwrap()) 
    } 
} 

fn main() { 
    let s: MyStruct = MyStruct::new(); 

    let vref = s.get_data_for(&3); 

    for x in vref.iter() { 
     println!("{}", x); 
    } 
} 

Ma to tę zaletę, że łatwo (poprzez metodę map na owning_ref), aby uzyskać podobne odniesienie do wszystkiego, co można uzyskać od Mutex (indywidualny element w postaci Vec itp.) nie trzeba ponownie wdrażać zwróconego typu.

+0

Biblioteka ma typ o nazwie 'MutexGuardRef', dlaczego nie używać tego? http://kimundi.github.io/owning-ref-rs/owning_ref/type.MutexGuardRef.html – Neikos

+0

Dzięki, nie zauważyłem 'MutexGuardRef'! –

+0

@ChrisEmerson Czy można zmodyfikować metodę 'get_data_for', aby nie" odpakować "wyniku funkcji' get' i zwróciło 'Option'? Próbowałem to zrobić sam, ale nie mogę obejść błędów typu "sprzeczne wymagania życia" – Sergey

3

Może to być możliwe dzięki użyciu dodatkowej struktury, która implementuje Deref i przechowuje MutexGuard.

Przykład:

use std::sync::{Arc, Mutex, MutexGuard}; 
use std::collections::HashMap; 
use std::ops::Deref; 

pub struct Inner<'a>(MutexGuard<'a, HashMap<i32, Vec<i32>>>, i32); 

impl<'a> Deref for Inner<'a> { 
    type Target = Vec<i32>; 
    fn deref(&self) -> &Self::Target { 
     self.0.get(&self.1).unwrap() 
    } 
} 
pub struct MyStruct { 
    data: Arc<Mutex<HashMap<i32, Vec<i32>>>>, 
} 

impl MyStruct { 
    pub fn get_data_for<'a>(&'a self, i: i32) -> Inner<'a> { 
     let d = self.data.lock().unwrap(); 
     Inner(d, i) 
    } 
} 

fn main() { 
    let mut hm = HashMap::new(); 
    hm.insert(1, vec![1,2,3]); 
    let s = MyStruct { 
     data: Arc::new(Mutex::new(hm)) 
    }; 

    { 
     let v = s.get_data_for(1); 
     println!("{:?}", *v); 
     let x : Vec<_> = v.iter().map(|x| x * 2).collect(); 
     println!("{:?}", x); // Just an example to see that it works 
    } 
} 
+0

Dzięki za odpowiedź! Postanowiłem oznaczyć odpowiedź Chrisa jako zaakceptowaną, ponieważ 'owning_ref' nie wymaga pośredniego typu. Ale co ważniejsze, jak Lukas wskazywał podobną sugestię w komentarzach, wywoła to zamknięcie 'map()' na każdym 'deref()'. Z drugiej strony, 'owning_ref' wywołuje wyszukiwanie tylko raz (przynajmniej w przypadkach, które przetestowałem). –

Powiązane problemy