2016-04-11 2 views
2

Ich habe eine IO-Bibliothek, die eine große Struktur State hat und ich schreibe eine Funktion, die zwei Phasen benötigt. In der ersten Phase wird nur die Leserklasse berührt, aber die Call-Site wählt eine schreibgeschützte Tabellenscheibe zum Übergeben.Wie aktualisiere ich meine Slicable-Eigenschaft, um die Borrow-Checker in wiederholten Aufrufen zu erfüllen

In der zweiten Phase wird die gesamte State-Struktur modifiziert, aber die schreibgeschützte Tabelle wird nicht mehr benötigt.

Ich habe die Funktion in zwei Funktionen aufgeteilt - und das funktioniert, aber wenn ich versuche, diese Funktionen zu kombinieren, bricht der borrow checker, als ich eine konkrete Vector Klasse mit einem benutzerdefinierten Merkmale Slicable

Ist ersetzen Gibt es einen Weg process_with_table_fn auf Slicable Werte in der State-Struktur statt direkt auf Vektoren zu machen?

tl; dr Ich möchte Fn what_i_want_to_work kompilieren, aber stattdessen habe ich nur what_works zu bauen. Ist meine Eigenschaftsdefinition für Slicable schlecht für diesen Anwendungsfall? Warum funktioniert der Betontyp besser als das Merkmal?

pub struct MemReader { 
    buf : [u8; 1024], 
    off : usize, 
} 
pub struct Vector<'a> { 
    buf : &'a [u32], 
} 
trait Slicable { 
    fn slice(self : &Self) -> &[u32]; 

} 
impl<'a> Slicable for Vector<'a> { 
    fn slice(self : &Self) -> &[u32]{ 
     return self.buf; 
    } 
} 
impl MemReader { 
    fn read(self : &mut Self, how_much : usize, output : &mut u8) -> bool { 
     if self.off + how_much > self.buf.len() { 
      return false; 
     } 
     self.off += how_much; 
     *output = self.buf[self.off - 1]; 
     return true; 
    } 
} 

pub struct State<'a> { 
    pub mr : MemReader, 
    pub translation_tables : [Vector<'a>; 4], 
    pub other_tables : [Vector<'a>; 4], 
    pub state : i32, 
} 

fn process_first(mr : &mut MemReader, table : &[u32]) -> (bool, u32) { 
    let mut temp : u8 = 0; 
    let ret = mr.read(8, &mut temp); 
    if !ret { 
     return (false, 0); 
    } 
    return (true, table[temp as usize]); 
} 

fn process_second(s : &mut State, ret_index : (bool, u32), mut outval : &mut u8) -> bool { 
    let (ret, index) = ret_index; 
    if ! ret { 
     return false; 
    } 
    s.state += 1; 
    return s.mr.read(index as usize, &mut outval); 
} 

pub fn process_with_table_fn(mut s : &mut State, table : &[u32], mut outval : &mut u8) -> bool { 
    let ret = process_first(&mut s.mr, table); 
    return process_second(&mut s, ret, &mut outval); 
} 

macro_rules! process_with_table_mac(
    ($state : expr, $table : expr, $outval : expr) => { 
     process_second(&mut $state, process_first(&mut $state.mr, &$table), &mut $outval) 
    }; 
); 

pub fn what_works(mut s : &mut State) { 
    let mut outval0 : u8 = 0; 
    let _ret0 = process_with_table_fn(&mut s, &s.other_tables[2].buf[..], &mut outval0); 
} 

/* 
pub fn what_i_want_to_work(mut s : &mut State) { 
    let mut outval0 : u8 = 0; 
    let ret0 = process_with_table_fn(&mut s, s.other_tables[2].slice(), &mut outval0); 

    // OR 

    let mut outval1 : u8 = 0; 
    //let ret1 = process_with_table_mac!(s, s.other_tables[2].slice(), outval1); 
} 
*/ 


fn main() { 

} 

Antwort

3

Es gibt zwei Dinge. Lässt bei Ihrer Eigenschaft Implementierung ersten Blick:

impl<'a> Slicable for Vector<'a> { 
    fn slice(self : &Self) -> &[u32]{ 
     return self.buf; 
    } 
} 

Die Unterschrift des Verfahrens wird auf

erweitert
fn slice<'b>(self : &'b Self) -> &'b[u32] 

was bedeutet, dass die Lebensdauer der resultierenden Scheibe kürzer ist als die Lebensdauer von self. Bei der Anrufstelle bedeutet dies, dass s.other_tables[2].slice() sich s leiht, während &s.other_tables[2].buf[..] etwas leiht, das Lebensdauer 'a hat, die Lebensdauer von s völlig ignorierend. Um dieses Verhalten zu replizieren, können Sie ein Leben lang zu Ihrer Eigenschaft hinzufügen:

trait Slicable<'a> { 
    fn slice(self: &Self) -> &'a [u32]; 
} 

impl<'a> Slicable<'a> for Vector<'a> { 
    fn slice(self: &Self) -> &'a [u32] { 
     self.buf 
    } 
} 

Jetzt sollten Sie festgelegt werden, aber der Compiler hat immer noch eine geringe Einschränkung in Bezug auf Lebensdauer Methodenaufruf, so dass Sie verteilen müssen, um Ihren Anruf in zwei Zeilen:

let slice = s.other_tables[2].slice(); 
let ret0 = process_with_table_fn(&mut s, slice, &mut outval0); 
Verwandte Themen