2016-10-10 2 views
2

Dieser Code ist eine Vereinfachung komplexen Code, um das Problem zu isolieren:Rust rekursiven Schwierigkeit

use std::marker::PhantomData; 

pub trait ExtWrite { 
    fn write_end<W>(&mut self, &mut W); 
} 

pub struct ST; 

impl ExtWrite for ST { 
    fn write_end<W>(&mut self, _: &mut W) {} 
} 

struct MCW<'a, 'b, W: 'a, EW: 'b + ExtWrite>(&'a mut W, &'b mut [EW]); 

impl<'a, 'b, W: 'a, EW: 'b + ExtWrite> MCW<'a, 'b, W, EW> { 
    fn write_end_all(&mut self) { 
     if let Some((f, last)) = self.1.split_first_mut() { 
      let mut el = MCW(self.0, last); 
      f.write_end(&mut el); 
      // do on current el.write_end(); 
     } 
    } 
} 

pub fn rec_test() { 
    let mut buff =(); 
    let v: Vec<TSW<ST>> = Vec::new(); 
    let mut el: TSW<ST> = TSW(Box::new(v), PhantomData); 
    el.write_end(&mut buff); 
} 

pub struct TSW<E: ExtWrite>(Box<Vec<TSW<E>>>, PhantomData<E>); 

impl<E: ExtWrite> ExtWrite for TSW<E> { 
    fn write_end<W>(&mut self, w: &mut W) { 
     let mut el = MCW(w, &mut self.0[..]); 
     el.write_end_all(); 
    } 
} 

fn main() {} 

führt zu dem folgenden Fehler:

error: reached the recursion limit while instantiating `<TSW<E> as ExtWrite><ST>::write_end::<MCW<MCW<MCW<MCW<MCW<MCW<MCW<MCW<MCW<MCW<MCW<MCW<MCW<MCW<MCW<MCW<MCW<MCW<MCW<MCW<MCW<MCW<MCW<MCW<MCW<MCW<MCW<MCW<MCW<MCW<MCW<MCW<MCW<MCW<MCW<MCW<MCW<MCW<MCW<MCW<MCW<MCW<MCW<MCW<MCW<MCW<MCW<MCW<MCW<MCW<MCW<MCW<MCW<MCW<MCW<MCW<MCW<MCW<MCW<MCW<MCW<MCW<MCW<MCW<MCW<(), TSW<ST>>, TSW<ST>>, TSW<ST>>, TSW<ST>>, TSW<ST>>, TSW<ST>>, TSW<ST>>, TSW<ST>>, TSW<ST>>, TSW<ST>>, TSW<ST>>, TSW<ST>>, TSW<ST>>, TSW<ST>>, TSW<ST>>, TSW<ST>>, TSW<ST>>, TSW<ST>>, TSW<ST>>, TSW<ST>>, TSW<ST>>, TSW<ST>>, TSW<ST>>, TSW<ST>>, TSW<ST>>, TSW<ST>>, TSW<ST>>, TSW<ST>>, TSW<ST>>, TSW<ST>>, TSW<ST>>, TSW<ST>>, TSW<ST>>, TSW<ST>>, TSW<ST>>, TSW<ST>>, TSW<ST>>, TSW<ST>>, TSW<ST>>, TSW<ST>>, TSW<ST>>, TSW<ST>>, TSW<ST>>, TSW<ST>>, TSW<ST>>, TSW<ST>>, TSW<ST>>, TSW<ST>>, TSW<ST>>, TSW<ST>>, TSW<ST>>, TSW<ST>>, TSW<ST>>, TSW<ST>>, TSW<ST>>, TSW<ST>>, TSW<ST>>, TSW<ST>>, TSW<ST>>, TSW<ST>>, TSW<ST>>, TSW<ST>>, TSW<ST>>, TSW<ST>>, TSW<ST>>>` 
    --> <anon>:40:3 
    | 
40 | fn write_end<W>(&mut self, w: &mut W) { 
    | ^

I Rust jede Nacht bin mit (9c31d76e9 2016- 10-03).

Der Code ist eine Struktur, die einen Zeiger auf ein Vec eines Arrays der gleichen Strukturart enthält. Diese verschachtelten Arrays werden rekursiv aufgerufen, um Schreibvorgänge in einem Writer zu übernehmen (die Attributzwangsbeschränkung W wurde entfernt, da sie für das Problem nicht relevant ist), und im echten Code wird die ExtWrite in einigen Fällen Writer.

Es gibt einen Ort, an dem Merkmalsauflösung funky führt zur Rekursion auf Typ, die Rekursion scheint ziemlich logisch, wenn Monomorphie in der Auflösung der W Eigenschaft berücksichtigt wird. MCW, abhängig von der Tiefe der Rekursion, wird eine unendliche Anzahl von möglichen Typen enthalten, aber das ist wirklich mit der Verwendung von MCW (im ursprünglichen Code benötigt), und die Tatsache, dass W Parameter von read_end Funktionen sind nicht mit dem verbunden Strukturdefinition, aber mit der unendlichen möglichen Variation dieser Funktion.

In diesem Snippet sollte W immer () und MCW immer sein.

Ein ähnlicher Fall, dass ich auf, wenn zur Vereinfachung der Suche:

struct IntSt<'a, W: 'a>(&'a W); 

pub fn comp_err() { 
    let w: u8 = 0; 
    rec(true, &w); 
} 

pub struct A(bool); 

fn rec<W>(b: bool, w: &W) { 
    if (b) { 
     rec(false, &IntSt(w).0); 
     //  rec(false, w) 
    } 
} 

fn main() {} 

Resultat:

error: reached the recursion limit while instantiating `rec::<&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&u8>` 
    --> <anon>:14:1 
    | 
14 | fn rec<W>(b: bool, w: &W) { 
    |^

Es verhält sich richtig mit diesem, aber ich sehe wirklich nicht, wie diese Art machen von ändern in meinem vorherigen Fall:

struct IntSt<'a, W: 'a>(&'a W); 

pub fn comp_err() { 
    let w: u8 = 0; 
    rec(true, &w); 
} 

pub struct A(bool); 

fn rec<W>(b: bool, w: &W) { 
    if (b) { 
     rec(false, IntSt(w).0); 
     //  rec(false, w) 
    } 
} 

fn main() {} 

Es ist wie MCW aussieht, Eine Lichtstruktur, die als temporärer Schreiber benutzt wird, führt zu Komplikationen mit ihrer Lebensdauer. Dies geschieht nur mit dem rekursiven Fall. Das scheint wirklich grenzwertig zu sein und ich weiß nicht, ob es mehr ein Bug oder eine zu erwartende Einschränkung ist. Ich habe auch versucht, höherrangige Merkmalsgrenzen zu verwenden, aber in diesem Fall arbeiten wir wirklich an Strukturen und meine wenigen Versuche waren erfolglos.

Ich konnte einfach TSW Redesign (in meinem realen Fall habe ich nur optional Zeiger auf eine Struktur, die Vec von TSW auf einer Ebene enthält) und einen TSW_2 ohne einen Zeiger auf die Vec vorstellen, aber es wäre wirklich schlecht fühlen (unbefriedigenden zumindest) als Lösung.

- Bearbeiten

Ja, danke Matthieu, das ist die richtige Diagnose (MCW misleaded mich (beim Testen ich es entfernt, und alles lief gut, aber es hat nicht die gleiche Sache tun):

- TSW<ST>::write_end<()> (&mut sel, w : &mut()) 
- MCW<(), TSW<ST>>::write_end_all(&mut self) 
- (f from slipt_first mut) 
     TSW<ST>::write_end<MCW<(),TSW<ST>> 
     MCW<MCW<(),TSW<ST>, > ... 

Wenn man über das ursprüngliche Problem nachdenkt, sollte der Typ genau ... sein. (die Elemente in der Vec sind Writers Erweiterung, die über die vorherigen Elemente der Vec (Art der geschichteten Writer) gelten sollte).

Im Nachhinein erinnere ich mich zuerst dieses Problem für die Verkettung meiner W und dann die Lösung mit einem Vec, um sie zu speichern (dann habe ich Single Type mit iterativen mehrschichtigen Schreiben), aber dann muss ich diese Vec verwenden, um zu schreiben einige Nutzdaten in früheren Vec und hier sollte ich die gleiche Grundregel und ein doppeltes Array verwenden). Aber ich habe es einfach mit einem fetten Zeiger über eine optionale Struktur versucht. Das ging nicht reibungslos, weil ich etwas wie "Option < Box < otherstructcontainingvec >>" gemacht habe, aber die andere Struktur, die vec enthält, ist kein Merkmal, und ähnlich ist in diesem Beispielcode Vec auch kein Merkmal.

Also ich hoffe, dass ich endlich meine Lösung: Vec < TSW < E >> (meine Struktur enthält es in Echt-Code) als Merkmal und einen echten Fett-Zeiger (mehr optimierte Lösung wäre Doppel-Dimension Vec (aber mein Anwendungsfall ist in der Tat eine einzige imbrication).

+1

Ich glaube nicht, dass Ihr Problem mit Lebenszeiten verbunden ist. Was rustc dir sagt, ist, dass es beim Instanziieren der Funktion 'write_end' in den rekursiven Aufruf in eine Endlosschleife geraten ist. Gibt es einen Grund, warum Sie hier Rekursion und nicht Iteration verwenden? –

Antwort

7

Sie haben ein Pingpong hier los ist, ohne dass ein Ende in Sicht.

  • Anruf TSW<E>::write_end<W>(&mut self, w : &mut W)
  • die MCW<W, TSW<E>>::write_end_all(&mut self)
  • ruft 10
  • die TSW<E>::write_end<MCW<W, TCW<E>>>(&mut self, w: &mut) nennt
  • die Anrufe ...

Jede neue Ebene der Rekursion Häufchen auf eine neue Art gibt, weshalb die Art in der Fehlermeldung so groß ist. Der rustc-Compiler, anstatt in eine Endlosschleife einzutreten, sagt Ihnen, dass Sie wahrscheinlich keine unendliche Anzahl von Funktionen instanziieren wollen.

Hier stimmt etwas mit Ihrer Logik nicht. Und das ist kein Leben.

Wenn Sie eine Rekursion starten, benötigen Sie einen Plan für eine Exit-Strategie: Sie sollte vorhersehbar enden.

Im speziellen Fall einer Rekursion in generischen Typen sollte es vorhersehbar enden, unabhängig von den Laufzeitwerten der Argumente.


Nicht zu wissen, was die richtige Logik, soll ich Ihnen eine iterative Lösung:

fn write_end_all(&mut self) { 
    for ew in self.1.iter_mut().rev() { 
     ew.write_end(self.0); 
     // do on current el.write_end(); 
    } 
} 

es ist viel einfacher, nicht einen Versuch verursacht eine unendliche Anzahl von Funktionen zu instanziiert und kann nicht die Funktionalität, die Sie überhaupt gesucht haben :)