2015-05-21 12 views
5

In rustc 1.0.0 möchte ich eine Funktion schreiben, die ein zweidimensionales Array mutiert, das vom Aufrufer bereitgestellt wird. Ich hatte gehofft, dies funktionieren würde:Veränderliches mehrdimensionales Array als Funktionsargument

fn foo(x: &mut [[u8]]) { 
    x[0][0] = 42; 
} 

fn main() { 
    let mut x: [[u8; 3]; 3] = [[0; 3]; 3]; 
    foo(&mut x); 
} 

Es schlägt fehl, zu kompilieren:

$ rustc fail2d.rs 
fail2d.rs:7:9: 7:15 error: mismatched types: 
expected `&mut [[u8]]`, 
    found `&mut [[u8; 3]; 3]` 
(expected slice, 
    found array of 3 elements) [E0308] 
fail2d.rs:7  foo(&mut x); 
        ^~~~~~ 
error: aborting due to previous error 

Ich glaube, das sagt mir, ich brauche irgendwie ein Stück Scheiben um die Funktion zu füttern, aber ich weiß nicht, wie konstruiere ich das?

Es "funktioniert", wenn ich die Länge des geschachtelten Arrays in der Funktionssignatur fest codiere. Dies ist nicht akzeptabel, weil die Funktion auf mehrdimensionalen Arrays beliebiger Dimension arbeiten soll:

fn foo(x: &mut [[u8; 3]]) { // FIXME: don't want to hard code length of nested array 
    x[0][0] = 42; 
} 

fn main() { 
    let mut x: [[u8; 3]; 3] = [[0; 3]; 3]; 
    foo(&mut x); 
} 

tldr; irgendwelche kostenlosen Möglichkeiten, einen Verweis auf ein mehrdimensionales Array zu übergeben, so dass die Funktion Anweisungen wie $ x [1] [2] = 3; $?

+0

Sie könnten in https://github.com/rust-lang/rfcs/issues/1038 und die entsprechende PR über generische Wert Parameter interessiert sein –

Antwort

6

Dies kommt auf eine Frage des Speicherlayouts. Nimmt man einen Typ T mit einer zur Kompilierzeit bekannten Größe an (diese Einschränkung kann geschrieben werden T: Sized), ist die Größe [T; n] zur Kompilierzeit bekannt (es benötigt n mal so viel Speicher wie T); aber [T] ist ein unselisierter Typ; seine Länge ist zur Kompilierzeit nicht bekannt. Daher kann es nur durch irgendeine Form von Indirektion verwendet werden, wie eine Referenz (&[T]) oder eine Box (Box<[T]>, obwohl dies von begrenztem praktischen Wert ist, mit Vec<T>, die Ihnen erlaubt, Elemente hinzuzufügen und zu entfernen, ohne jedes Mal neu zuweisen zu müssen durch Überlagerung).

Eine Scheibe eines unselisierten Typs macht keinen Sinn; es ist erlaubt aus Gründen, die mir nicht klar sind, aber Sie können nie wirklich eine Instanz davon haben. (Vec<T> durch Vergleich erfordert T: Sized.)

&[T; n] zu &[T] coerce kann und &mut [T; n] zu &mut [T], aber dies gilt nur an der äußersten Ebene; Der Inhalt des Slice ist fixiert (Sie müssen ein neues Array oder einen neuen Vektor erstellen, um eine solche Transformation zu erreichen, da das Speicherlayout jedes Elements unterschiedlich ist). Dies hat zur Folge, dass Arrays für eindimensionale Arbeit arbeiten, bei mehrdimensionaler Arbeit jedoch auseinanderfallen. Arrays sind derzeit sehr zweitklassige Bürger in Rust, und das wird so lange dauern, bis die Sprache es unterstützt, Scheiben generisch über die Länge zu bringen, was wahrscheinlich ist.

Ich empfehle, dass Sie entweder ein eindimensionales Array (geeignet für quadratische Matrizen, indexiert durch x * width + y oder ähnlich) oder Vektoren (Vec<Vec<T>>) verwenden. Es kann auch Bibliotheken geben, die bereits über eine geeignete Lösung abstrahieren.