2015-03-11 12 views
5

Ich habe versucht, IntoIterator für [T; N] zu implementieren. Ich schrieb eine völlig sichere Version mit Default und swap (PlayPen). Dann habe ich es portiert, um uninitialized, ptr::copy, Drop und forget (PlayPen) zu verwenden. Meine Iterator Struktur sieht wie folgt aus:Was ist der Unterschied zwischen [T; N] und U wenn U immer auf [T; N]?

struct IntoIter<T> { 
    inner: Option<[T; N]>, 
    i: usize, 
} 
impl<T> Iterator for IntoIter<T> { ... } 

Da ich kein Iterator struct pro Wert von N erstellen wollte, änderte ich die Struktur zu

struct IntoIter<U> { 
    inner: Option<U>, 
    i: usize, 
} 
impl<T> Iterator for IntoIter<[T; N]> { ... } 

Offensichtlich ich die Iterator einstellen musste und Drop Implementierungen (PlayPen).

Aber jetzt habe ich irgendwie undefiniertes Verhalten eingeführt. Panics passieren oder nicht abhängig von println s, Optimierungsstufen oder Tierkreiszeichen.

thread '<main>' panicked at 'index out of bounds: the len is 5 but the index is 139924442675478', <anon>:25 
thread '<main>' panicked at 'index out of bounds: the len is 5 but the index is 140451355506257', <anon>:25 
application terminated abnormally with signal 4 (Illegal instruction) 

Entweder meine zweite Implementierung bereits undefiniertes Verhalten zeigt, oder es gibt einen Unterschied zwischen den zweiten und dritten Implementierungen. Beim Betrachten des generierten (nicht optimierten) LLVM-IR habe ich festgestellt, dass die einzigen grundlegenden Unterschiede in der dritten Version auftreten, die mit [[Box<i32>; 5]; 5] Typen endet. Ich kann sehen, wie ich versehentlich einen solchen Typ erstellen könnte, aber ich habe speziell die dritte Version für einen solchen Fehler überprüft und kann ihn nicht finden.

+0

Oh, vergiss es dann weitermachen dann. – delnan

+0

die sichere Version mit dem U als [T; N] funktioniert gut: http://is.gd/enk0I3 –

Antwort

2

Ich glaube, dass Sie einen Fehler mit #[unsafe_destructor] schlagen. Ich reduziert Ihren Code dies:

#![feature(unsafe_destructor)] 

struct IntoIter<U> { 
    inner: Option<U>, 
} 

impl<T> Iterator for IntoIter<[T; 8]> { 
    type Item = T; 
    fn next(&mut self) -> Option<T> { None } 
} 

#[unsafe_destructor] 
impl<T> Drop for IntoIter<[T; 8]> { 
    fn drop(&mut self) { 
     // destroy the remaining elements 
     for _ in self.by_ref() {} 

     unsafe { std::intrinsics::forget(self.inner.take()) } 
    } 
} 

fn main() { 
    let arr = [1; 8]; 
    IntoIter { inner: Some(arr) }; 
} 

ich dann kompiliert (rustc -g unsafe.rs) und lief es in rust-lldb. Ich habe einen Breakpoint auf die Drop Implementierung und self ausgedruckt:

(lldb) p self 
(unsafe::IntoIter<[[i32; 8]; 8]> *) $0 = &0x7fff5fbff568 

Sie können sehen, dass es denkt, dass der Typ-Parameter ein Array von Arrays ist, genau wie Sie bemerkt. An dieser Stelle werden wir Speicher löschen, wenn wir tatsächlich fallen. Ich glaube, dass Rust Speicher immer noch auf Null setzt, also könnten wir vielleicht Nullen über einen beliebigen Teil des Speichers schreiben.

Für eine gute Maßnahme:

rustc --verbose --version 
rustc 1.0.0-dev (cfea8ec41 2015-03-10) (built 2015-03-10) 
binary: rustc 
commit-hash: cfea8ec41699e25c8fb524d625190f0cb860dc71 
commit-date: 2015-03-10 
build-date: 2015-03-10 
host: x86_64-apple-darwin 
release: 1.0.0-dev 
+0

Ich habe den entsprechenden Fehler gefunden: https://github.com/rust-lang/rust/issues/8142 –

Verwandte Themen