2015-02-09 18 views
5

Próbuję nauczyć się Rusta, ale jedyną rzeczą, którą robię, jest uderzanie w ścianę, próbując zsunąć znajome (dla mnie) koncepcje Javy do jej systemu typów. Lub spróbuj wymyślić koncepcje Haskella, itp.Jak reprezentować stan wspólnej zmienności?

Chcę napisać grę z Player i wielu Resource s. Każdy Resource może być własnością jednej Player:

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; 
} 

Nie skompilować, bo nie mogę mieć punkt zasobów do odtwarzacza, a jednocześnie modyfikując go:

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 

Ponadto , jeśli Resource ma zmienne odwołanie do Player, nie mogę mieć dwóch Resource s z tym samym właścicielem.

Jaki jest Rust sposób rozwiązania takich przypadków?


ja uproszczony moje pytanie, a jednocześnie odpowiedź Shepmaster jest poprawna odpowiedź na to, że nie jest to, co chciałem dostać (bo co prosiłem nie było to, co naprawdę chciałem zapytać). Spróbuję przeformułować to i dodać więcej kontekstu.

  1. Zasoby są połączone w jakiś sposób - mapa wszystkich zasobów tworzy (nie) skierowany wykres.
  2. Każdy gracz może posiadać wiele zasobów, każdy zasób może należeć do jednego gracza. Gracz powinien mieć możliwość zdobycia punktów z posiadanych przez siebie zasobów. Myślałem o podpisie: fn addPoints(&mut self, allResources: &ResourcesMap) ->().
  3. Gracz może przejąć zasób połączony z jednym ze swoich zasobów od innego gracza. Może to spowodować utratę punktów dla drugiego gracza.

Problemy:

  1. Jak do reprezentowania tych wykres w Rust (ewentualnie struktury cyklicznej, gdzie każdy węzeł może być skierowany do wielu węzłów)?
  2. Pierwotny problem: jeśli Resource wskazuje na Player, nie mogę zmodyfikować odtwarzacza!

Resource s punkt Player ponieważ - naturalny sposób na takiej operacji byłoby zacząć od niektórych zasobów gracz A jest, poruszać się po mapie do B zasobu gracza i od tego zasobu do gracza B odjąć punkty. Po prostu nie wydaje się to naturalne w Rust (przynajmniej dla mnie).

Odpowiedz

6

The cell documentation page ma raczej dobre przykłady. Rust zawsze będzie starał się ochronić cię przed robieniem złych rzeczy (jak posiadanie dwóch zmiennych odniesień do tego samego). Dlatego nie jest to tak "łatwe" jak używanie wbudowanych referencji Rusta, ponieważ musisz wykonać sprawdzanie runtime (referencje Rust są sprawdzane podczas kompilacji).

Typ RefCell istnieje właśnie w tym celu. Sprawdza zasady zmienności w czasie wykonywania. Dostaniesz trochę pamięci i czasu obliczeń, ale skończysz z tą samą pamięcią - bezpieczeństwem, które Rust obiecuje w swoich testach kompilacji.

Twój przykład przeniesiony do RefCell wygląda następująco.

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()); 
} 

Moim problemem jest to, czy to, co próbuję zrobić próbuje napisać kod Java w Rust, można to zrobić w sposób rdzę bez poświęcania czasu kompilacji bezpieczeństwo? Unikaj w ogóle tego wspólnego, zmieniającego się stanu?

Nie poświęcasz czasu na kompilację. Rust upewnia się (podczas kompilacji), że poprawnie używasz swoich bibliotek. Mimo to Twój program może paniki w czasie wykonywania, jeśli używasz funkcji borrow*. Jeśli zamiast tego używasz funkcji try_borrow*, możesz sprawdzić, czy udało się, a jeśli nie, wykonać operację zastępczą.

Można również użyć pola z policzeniem referencyjnym dla numeru RefCell(). Musisz tylko upewnić się, że nie tworzysz cykli, lub Twoja pamięć nigdy nie zostanie uwolniona. Byłoby to o wiele bardziej podobne do Javy (chociaż Java automatycznie znajduje cykle).

+0

Brzmi nieźle. Martwię się, jeśli to, co próbuję zrobić, to napisać kod Java w Rust, czy można to zrobić w sposób Rust, bez poświęcania czasu na kompilację? Unikaj w ogóle tego wspólnego, zmieniającego się stanu? –

+0

Uwaga: nie poświęcasz czasu na kompilację. Rust upewnia się (podczas kompilacji), że poprawnie używasz swoich bibliotek. Mimo to twój program może panikować w czasie wykonywania, jeśli używasz funkcji pożyczania *. Jeśli zamiast tego użyjesz funkcji try_borrow *, możesz sprawdzić, czy udało się, a jeśli nie, wykonać operację zastępczą. –

+1

Możesz również użyć pola licznika odwołań (http://doc.rust-lang.org/std/rc/index.html) do RefCell do swojego typu. Musisz tylko upewnić się, że nie tworzysz cykli, lub Twoja pamięć nigdy nie zostanie uwolniona. To byłoby o wiele bardziej podobne do java (chociaż java automatycznie znajduje cykle) –

4

Każdy zasób może należeć do jednego gracza.

Bądź rodzaje zrobić wtedy:

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()); 
} 

Edit Dzięki @ziggystar za wskazanie moją oryginalną wersję pozwoliły graczom tylko jeden Resource. Teraz gracze mogą posiadać zasoby N, ale nadal są jedynym właścicielem zasobu.

+2

Teraz każdy gracz ma dokładnie jeden zasób. To nie to samo. (Nie przeczytałem pytania) – ziggystar

+1

Zmieniłem moje pytanie, ponieważ w sposób, w jaki to powiedziałem, poprawna odpowiedź jest tą, którą mi dałeś, ale nie porusza mnie to w swoim zrozumieniu Rdzy. –

+0

jaki jest preferowany sposób pod względem stylu kodu tworzenia instancji struktury: 'Player {' lub 'Player {' (z białymi znakami)? –

Powiązane problemy