2015-02-09 15 views
5

Ich versuche Rust zu lernen, aber das einzige, was ich tue, ist, immer wieder gegen die Wand zu schlagen, um mir vertraute (mir) Java-Konzepte in sein Typsystem zu schaufeln. Oder versuchen, Haskell Konzepte, etc. zu schüren.Wie wird der gemeinsame veränderbare Zustand dargestellt?

Ich möchte ein Spiel mit einem Player und viele Resource s schreiben. Jeder Resource kann von einem Player besessen werden:

struct Player { 
    points: i32, 
} 

struct Resource<'a> { 
    owner: Option<&'a Player>, 
} 

fn main() { 
    let mut player = Player { points: 0 }; 
    let mut resources = Vec::new(); 
    resources.push(Resource { 
     owner: Some(&player), 
    }); 
    player.points = 30; 
} 

Es ist nicht zu kompilieren, weil ich nicht die Ressource Punkt Spieler haben kann, während es gleichzeitig ändern:

error[E0506]: cannot assign to `player.points` because it is borrowed 
    --> src/main.rs:15:5 
    | 
13 |   owner: Some(&player), 
    |      ------ borrow of `player.points` occurs here 
14 |  }); 
15 |  player.points = 30; 
    |  ^^^^^^^^^^^^^^^^^^ assignment to borrowed `player.points` occurs here 

Außerdem , wenn die Resource einen veränderlichen Verweis auf Player besaß, konnte ich nicht sogar zwei Resource s mit dem gleichen Inhaber haben.

Was ist der Weg von Rust, um solche Fälle zu lösen?


stark vereinfachte ich meine Frage, und während Shepmaster Antwort eine richtige Antwort darauf ist, es ist nicht das, was ich (weil das, was ich gefragt war nicht das, was ich wirklich fragen wollte) erhalten wollte. Ich werde versuchen, es umzuformulieren und mehr Kontext hinzuzufügen.

  1. Die Ressourcen sind in irgendeiner Weise verbunden - die Karte aller Ressourcen bildet einen (un) gerichteten Graphen.
  2. Jeder Spieler kann viele Ressourcen besitzen, jede Ressource kann einem Spieler gehören. Der Spieler sollte in der Lage sein, Punkte aus Ressourcen zu erhalten, die er besitzt. Ich dachte an eine Unterschrift wie: fn addPoints(&mut self, allResources: &ResourcesMap) ->().
  3. Der Spieler kann eine Ressource übernehmen, die mit einer seiner Ressourcen von einem anderen Spieler verbunden ist. Es könnte einige Punkteverlust für den anderen Spieler ergeben.

Probleme:

  1. Wie solche Graphen in Rust (eine möglicherweise cyclischer Struktur, wobei jeder Knoten aus zu vielen Knoten gerichtet werden kann) darstellen? Das ursprüngliche Problem: Wenn die Resource auf eine Player zeigt, kann ich den Player nicht ändern!

Resource s Punkt zu Player da - der natürliche Weg, eine solche Operation zu tun wäre, von einigen der Spieler A Ressourcen zu beginnen, eines Spielers B Ressource durch die Karte zu bewegen und von dieser Ressource zu Spieler B zu subtrahieren die Punkte. Es scheint in Rust einfach nicht natürlich zu sein (zumindest für mich).

Antwort

6

Die cell documentation page hat ziemlich gute Beispiele. Rust wird immer versuchen, dich davor zu schützen, schlechte Dinge zu tun (wie zwei veränderliche Verweise auf dasselbe). Deshalb ist es nicht ganz so einfach wie die eingebauten Referenzen von Rust, da Sie die Laufzeitprüfung durchführen müssen (Rust-Referenzen werden zur Kompilierungszeit überprüft).

Der Typ RefCell existiert nur dafür. Es prüft die Veränderbarkeitsregeln zur Laufzeit. Sie werden etwas Speicher- und Rechenzeit-Overhead bekommen, aber Sie haben die gleiche Speicher-Sicherheit, die Rust in seinen Kompilierungs-Checks verspricht.

Ihr Beispiel ported zu RefCell sieht wie folgt aus.

use std::cell::RefCell; 

struct Player { 
    points: i32, 
} 

// the lifetime is still needed to guarantee that Resources 
// don't outlive their player 
struct Resource<'a> { 
    owner: &'a RefCell<Player>, 
} 

impl<'a> Resource<'a> { 
    fn test(&self) -> i32 { 
     self.owner.borrow().points 
    } 
} 

fn main() { 
    let player = RefCell::new(Player { points: 0 }); 
    let mut resources = Vec::new(); 
    resources.push(Resource { owner: &player }); 
    player.borrow_mut().points = 30; 
    println!("{:?}", resources[0].test()); 
} 

Meine Sorge ist, ob das, was ich versuche zu tun versuchen, Java-Code in Rust zu schreiben, kann es in einer Rost Weise erfolgen ohne Zeit Abstriche bei der Sicherheit kompilieren? Vermeiden Sie diesen gemeinsamen veränderlichen Zustand überhaupt?

Sie verzichten nicht auf Kompilierzeitsicherheit. Rust stellt sicher (zur Kompilierzeit), dass Sie Ihre Bibliotheken korrekt verwenden. Dennoch könnte Ihr Programm Panik zur Laufzeit, wenn Sie die borrow* Funktionen verwenden. Wenn Sie stattdessen die Funktionen try_borrow* verwenden, können Sie überprüfen, ob dies erfolgreich war, und falls nicht, führen Sie einen Fallback-Vorgang aus.

Sie können auch eine Referenzzahlbox eines RefCell zu Ihrem Typ (Rc<RefCell<Player>>) verwenden. Dann müssen Sie nur sicherstellen, dass Sie keine Zyklen erstellen, oder Ihr Speicher wird nie freigegeben werden. Dies wäre viel mehr Java (obwohl Java automatisch Zyklen findet).

+0

Klingt gut. Meine Sorge ist, wenn ich versuche, Java-Code in Rust zu schreiben, kann es in einer Rust-Weise getan werden, ohne Kompilierungszeit-Sicherheit zu opfern? Vermeiden Sie diesen gemeinsamen veränderlichen Zustand überhaupt? –

+0

Hinweis: Sie verzichten nicht auf Kompilierzeitsicherheit. Rust stellt sicher (zur Kompilierzeit), dass Sie Ihre Bibliotheken korrekt verwenden. Dennoch kann Ihr Programm zur Laufzeit in Panik geraten, wenn Sie die borrow * -Funktionen verwenden. Wenn Sie stattdessen die try_borrow * -Funktionen verwenden, können Sie überprüfen, ob dies erfolgreich war, und falls nicht, sollten Sie einen Fallback-Vorgang ausführen. –

+1

Sie können auch eine Referenz-Box (http://doc.rust-lang.org/std/rc/index.html) zu einer RefCell zu Ihrem Typ verwenden. Dann müssen Sie nur sicherstellen, dass Sie keine Zyklen erstellen, oder Ihr Speicher wird nie freigegeben werden. Dies wäre viel mehr Java (obwohl Java automatisch Zyklen findet) –

4

Jede Ressource kann von einem Spieler besessen werden.

Machen Sie die Typen tun, dann:

struct Player { 
    points: i32, 
    resources: Vec<Resource>, 
} 

struct Resource { 
    gold: i32, 
} 

fn main() { 
    let player1 = Player { 
     points: 30, 
     resources: vec![Resource { gold: 54 }], 
    }; 
    let player2 = Player { 
     points: 50, 
     resources: vec![Resource { gold: 99 }], 
    }; 

    // If you really need an array of all the resources... 
    // Although this seems like you should just ask the Player to do something 
    let mut resources: Vec<_> = vec![]; 
    resources.extend(player1.resources.iter()); 
    resources.extend(player2.resources.iter()); 
} 

bearbeiten Dank für den Hinweis auf meine ursprüngliche Version erlaubt die Spieler nur eine Resource müssen @ziggystar. Spieler können jetzt N Ressourcen besitzen, aber sie sind immer noch der einzige Besitzer einer Ressource.

+2

Jetzt besitzt jeder Spieler genau eine Ressource. Das ist nicht das Gleiche. (Ich habe die Frage nicht gelesen) – ziggystar

+1

Ich habe meine Frage bearbeitet, denn in der Art und Weise, wie ich es gesagt habe, ist die richtige Antwort diejenige, die Sie mir gegeben haben, aber sie bringt mich in meinem Verständnis von Rust kaum voran. –

+0

Was ist der bevorzugte Weg in Bezug auf den Code-Stil zum Erstellen einer Instanz einer Struktur: 'Player {' oder 'Player {' (mit dem Leerzeichen)? –

Verwandte Themen