Najprostszym sposobem Ten model jest użyć parametru typu phantom na Storage
który działa jako unikalny ID, tak jak poniżej:
use std::kinds::marker;
pub struct Storage<Id, T> {
marker: marker::InvariantType<Id>,
vec: Vec<T>
}
impl<Id, T> Storage<Id, T> {
pub fn new() -> Storage<Id, T>{
Storage {
marker: marker::InvariantType,
vec: Vec::new()
}
}
pub fn get<'r>(&'r self, h: &Handle<Id, T>) -> &'r T {
let index = h.id;
&self.vec[index]
}
pub fn set(&mut self, h: &Handle<Id, T>, t: T) {
let index = h.id;
self.vec[index] = t;
}
pub fn create(&mut self, t: T) -> Handle<Id, T> {
self.vec.push(t);
Handle {
marker: marker::InvariantLifetime,
id: self.vec.len() - 1
}
}
}
pub struct Handle<Id, T> {
id: uint,
marker: marker::InvariantType<Id>
}
fn main() {
struct A; struct B;
let mut s1 = Storage::<A, uint>::new();
let s2 = Storage::<B, uint>::new();
let handle1 = s1.create(5);
s1.get(&handle1);
s2.get(&handle1); // won't compile, since A != B
}
ten rozwiązuje problem w najprostszym przypadku, ale ma pewne wady. Głównie zależy to od użycia, aby zdefiniować i wykorzystać wszystkie te typy fantomów i udowodnić, że są unikatowe. Nie zapobiega złemu zachowaniu użytkownika, gdy może używać tego samego typu widmowego dla wielu instancji Storage
. W dzisiejszej Rust jest to jednak najlepsze, co możemy zrobić.
Alternatywne rozwiązanie, które nie działa dzisiaj z przyczyn, które dostanę później, ale może zadziałać później, wykorzystuje okresy życia jako anonimowe typy identyfikatorów. Ten kod używa znacznika InvariantLifetime
, który usuwa wszystkie relacje podpisywania z innymi cyklami życia przez całe życie.
Oto ten sam system, przepisany do korzystania InvariantLifetime
zamiast InvariantType
:
use std::kinds::marker;
pub struct Storage<'id, T> {
marker: marker::InvariantLifetime<'id>,
vec: Vec<T>
}
impl<'id, T> Storage<'id, T> {
pub fn new() -> Storage<'id, T>{
Storage {
marker: marker::InvariantLifetime,
vec: Vec::new()
}
}
pub fn get<'r>(&'r self, h: &Handle<'id, T>) -> &'r T {
let index = h.id;
&self.vec[index]
}
pub fn set(&mut self, h: &Handle<'id, T>, t: T) {
let index = h.id;
self.vec[index] = t;
}
pub fn create(&mut self, t: T) -> Handle<'id, T> {
self.vec.push(t);
Handle {
marker: marker::InvariantLifetime,
id: self.vec.len() - 1
}
}
}
pub struct Handle<'id, T> {
id: uint,
marker: marker::InvariantLifetime<'id>
}
fn main() {
let mut s1 = Storage::<uint>::new();
let s2 = Storage::<uint>::new();
let handle1 = s1.create(5);
s1.get(&handle1);
// In theory this won't compile, since the lifetime of s2
// is *slightly* shorter than the lifetime of s1.
//
// However, this is not how the compiler works, and as of today
// s2 gets the same lifetime as s1 (since they can be borrowed for the same period)
// and this (unfortunately) compiles without error.
s2.get(&handle1);
}
W hipotetycznej przyszłości, cesja wcieleń może się zmienić i możemy rozwijać lepszy mechanizm tego rodzaju znakowania. Jednak na razie najlepszym sposobem na osiągnięcie tego jest typ fantomowy.
Myślę, że możesz użyć znacznika ['ContravariantLifetime'] (http://doc.rust-lang.org/core/kinds/marker/struct.ContravariantLifetime.html). – Simple
@ Prosto Myślałem o użyciu ContravariantLifetime, ale czasy życia z s1 i s2 powinny być takie same, więc myślę, że to nie zadziała, ale dam mu szansę. –