I'm new to rust, so forgive me if the question is naive.
I'm trying to build an OS in rust and I'm following this tutorial. The OS doesn't have memory management yet, so the goal is to build an object which is like a vector in that it can be pushed and popped etc, but it lives on the stack. We do this by initializing it with an array of fixed size. It looks like this:
#[derive(Debug)]
pub struct StackVec<'a, T: 'a> {
storage: &'a mut [T],
len: usize
}
impl<'a, T: 'a> StackVec<'a, T> {
pub fn new(storage: &'a mut [T]) -> StackVec<'a, T> {
StackVec {
storage: storage,
len: 0,
}
}
pub fn with_len(storage: &'a mut [T], len: usize) -> StackVec<'a, T> {
if len > storage.len(){
panic!();
}
StackVec{
storage: storage,
len: len
}
}
pub fn capacity(&self) -> usize {
self.storage.len()
}
pub fn into_slice(self) -> &'a mut [T] {
&mut self.storage[0..self.len]
}
// Other functions which aren't relevant for the question.
}
Popping and pushing increases and decreases the len variable and adds and removes entries from the appropriate place in the array.
Now, we also need to implement the IntoIterator trait. Given that the StackVec contains a reference to an array, I thought that I could just return an iterator from the underlying array:
impl <'a, T:'a> IntoIterator for StackVec<'a, T> {
type Item = T;
type IntoIter = core::array::IntoIter; // <- Throws "not found in `core::array"
fn into_iter(self) -> Self::IntoIter {
self.into_slice().into_iter()
}
}
But no matter how much I play around with it, it still doesn't want to compile. I can't find a way to express using types that into_itershould return the iterator for the array. What am I doing wrong?
Different problems here:
You cannot use array::IntoIterator because you do not have an array, you have a slice, which is quite different. It can be solved, for example, by using the proper core::slice::Iter as in the example.
You are trying to return T but in reality you only give access to &T, so return Item should be &T
Your into_slice method uses a &mut which is not necessary, you can reslice the storage for this implementation.
impl <'a, T:'a> IntoIterator for StackVec<'a, T> {
type Item = &'a T;
type IntoIter = core::slice::Iter<'a, T>;
fn into_iter(self) -> Self::IntoIter {
self.storage[0..self.len].into_iter()
}
}
Playground
Related
I am trying to avoid lifetimes because I still don't have a good understanding of the concept. I am reading this wonderful article and it clarifies many misunderstandings. Although I am not sure I can solve the problem.
I know how to implement iterable collection on dyn. Playground:
use std::collections::HashMap;
pub trait Enumerable {
fn elements<'a, 'b>(&'a self) -> Box<dyn Iterator<Item = (i32, &String)> + 'b>
where
'a: 'b;
}
#[derive(Debug)]
struct Container {
pub map: HashMap<i32, String>,
}
impl Enumerable for Container {
fn elements<'a, 'b>(&'a self) -> Box<dyn Iterator<Item = (i32, &String)> + 'b>
where
'a: 'b,
{
Box::new(self.map.iter().map(|el| (*el.0, el.1)))
}
}
My attempt to implement the same code without dyn. Playground:
use std::collections::HashMap;
pub trait Enumerable<'it, 'it2, It>
where
It: Iterator<Item = &'it2 (i32, &'it String)>,
'it: 'it2,
{
fn elements<'a, 'b>(&'a self) -> It
where
'a: 'b;
}
#[derive(Debug)]
struct Container {
pub map: HashMap<i32, String>,
}
impl<'it, 'it2> Enumerable<'it, 'it2, core::slice::Iter<'it, (i32, &String)>> for Container {
fn elements<'a, 'b>(&'a self) -> core::slice::Iter<'a, (i32, &'b String)>
where
'a: 'b,
{
self.map.iter().map(|el| (*el.0, el.1))
}
}
I was thinking about using impls but there is a restriction on using it in a trait. What is wrong with the code? What other useful articles can you recommend?
Apart of the fact that I don't think you need two separate lifetimes 'a and 'b, your first code example already looks quite promising.
Then, once you have only one lifetime, Rust can figure out lifetimes without any annotations:
use std::collections::HashMap;
pub trait Enumerable {
fn elements(&self) -> Box<dyn Iterator<Item = (i32, &String)> + '_>;
}
#[derive(Debug)]
struct Container {
pub map: HashMap<i32, String>,
}
impl Enumerable for Container {
fn elements(&self) -> Box<dyn Iterator<Item = (i32, &String)> + '_> {
Box::new(self.map.iter().map(|el| (*el.0, el.1)))
}
}
I know this is an XY-problem answer, but maybe it helps anyway. Your main reasoning behind not using dyn was to not deal with lifetimes, so I thought this might be relevant.
There is a solution to the original problem with GAT and TAIT which are not part of stable channgel for today.
Solution is
mod mod1 {
pub trait Enumerable {
type It<'it>: Iterator<Item = (i32, &'it String)>
where
Self: 'it;
fn elements(&self) -> Self::It<'_>;
}
}
//
impl mod1::Enumerable for Container {
type It<'it> = impl Iterator<Item = (i32, &'it String)>;
fn elements(&self) -> Self::It<'_> {
self.map.iter().map(|el| (*el.0, el.1))
}
}
Full solution
There are alternative solutions, but this one works even if the trait is not part of your crate.
Also, I should note, that if possible to avoid using lifetimes you can implement IntoIterator for your &Container:
impl< 'it > IntoIterator for &'it Container
{
type Item = ( &'it i32, &'it String );
type IntoIter = std::collections::hash_map::Iter< 'it, i32, String >;
fn into_iter( self ) -> Self::IntoIter
{
self.map.iter()
}
}
Full solution of a tweaked problem
Because the lifetime is dropped that works even on stable Rust.
Most probably you want to have your own InotIterator-like trait, especially if there is more than a single way how can you iterate your container, but if not you can simply implement standard IntoIterator for reference.
I have a custom collection like this:
struct VecChoice<T> {
v1: Vec<T>,
v2: Vec<T>,
use_v1: Vec<bool>,
}
in the impl I can iterate this collection like this:
fn foo(&self, ...) {
let item_refs: Vec<_> = (0..self.v1.len()).map(|i| {
if self.use_v1[i] {
&self.v1[i]
} else {
&self.v2[i]
}
});
// ... do whatever I want with chosen references
}
However, I am failing to make it iterable:
impl<'a, T> IntoIterator for &'a VecChoice<T> {
type Item = &'a T;
// this fails because the trait `Sized` is not implemented for `(dyn FnMut(usize) -> Self::Item + 'static)`
type IntoIter = Map<usize, dyn FnMut(usize) -> Self::Item>;
fn into_iter(self) -> Self::IntoIter {
(0..self.v1.len()).map(|i| {
if self.use_v1[i] {
&self.v1[i]
} else {
&self.v2[i]
}
})
}
}
I could probably collect results into a Vec<&T> as above, then use its into_iter, but I suspect there should be a way to do it without constructing intermediate Vec.
The closure that you have passed to map actually does have a size. The problem though is that this type isn't nameable. You've tried to solve that with dyn, which isn't quite the right solution because the closure is sized but dyn makes it so that it isn't. dyn would be appropriate if there were different possible sizes, but then you'd have to put it behind a pointer of some kind so that the IntoIter type is Sized.
This is one of those cases where it is probably better to implement the Iterator manually, rather than using combinators.
struct VecChoiceIter<'a, T> {
index: usize,
vec_choice: &'a VecChoice<T>,
}
impl<'a, T> Iterator for VecChoiceIter<'a, T> {
type Item = &'a T;
fn next(&mut self) -> Option<Self::Item> {
if self.index == self.vec_choice.v1.len() {
None
} else {
let i = self.index;
self.index += 1;
let use_v1 = self.vec_choice.use_v1[i];
if use_v1 {
Some(&self.vec_choice.v1[i])
} else {
Some(&self.vec_choice.v2[i])
}
}
}
}
This gives you a Sized and nameable type that you can use for the IntoIterator implementation:
impl<'a, T> IntoIterator for &'a VecChoice<T> {
type Item = &'a T;
type IntoIter = VecChoiceIter<'a, T>;
fn into_iter(self) -> Self::IntoIter {
VecChoiceIter { index: 0, vec_choice: self }
}
}
There are some interesting RFCs in progress that could make this work more like how you originally wanted. In particular RFC-2515. This would let you write your IntoIterator implementation as you originally tried, but without having to name the type (playground - nightly):
impl<'a, T> IntoIterator for &'a VecChoice<T> {
type Item = &'a T;
// This is an "existential" type. That is, tell the compiler that there is
// exactly one possibility for what this type can be, which it can infer
// from the usage.
type IntoIter = impl Iterator<Item = Self::Item>;
fn into_iter(self) -> Self::IntoIter {
(0..self.v1.len()).map(move |i| {
if self.use_v1[i] {
&self.v1[i]
} else {
&self.v2[i]
}
})
}
}
It's often very tempting to try to make an iterator out of a pre-made collection, but unfortunately this tends to run into a practical problem a lot of the time: you need some way to store an offset into that collection, so you serve the right chunk of data out of it when next is called. Consequently, you almost always need to provide some custom iterator type.
In this case, you can do so like this:
struct VecChoice<T> {
v1: Vec<T>,
v2: Vec<T>,
use_v1: Vec<bool>,
}
struct VecChoiceIter<'a, T> {
off: usize,
collection: &'a VecChoice<T>,
}
impl<'a, T> Iterator for VecChoiceIter<'a, T> {
type Item = &'a T;
fn next(&mut self) -> Option<Self::Item> {
let off = self.off;
self.off += 1;
if *self.collection.use_v1.get(off)? {
self.collection.v1.get(off)
} else {
self.collection.v2.get(off)
}
}
}
impl<'a, T> IntoIterator for &'a VecChoice<T> {
type Item = &'a T;
type IntoIter = VecChoiceIter<'a, T>;
fn into_iter(self) -> Self::IntoIter {
VecChoiceIter {
off: 0,
collection: self,
}
}
}
Note that in this case, I've switched use_v1 to a Vec<bool>, because this is not C and only booleans can be used in conditionals.
You could also do the conversion up front and store it in its own Vec, but in my experience people don't expect creating an iterator, whether by calling iter or into_iter, to be expensive. Iterators are pretty fundamental in Rust, and as a consequence it's very common for folks to create lots of them, often implicitly, and making those functions be expensive would be undesirable in many cases.
Probably the most simple way is to use .zip() and return an opaque impl Iterator from a method on the type (so you don't have to write out the actual type):
struct VecChoice<T> {
v1: Vec<T>,
v2: Vec<T>,
use_v1: Vec<bool>,
}
impl<T> VecChoice<T> {
fn iter(&self) -> impl Iterator<Item = &T> {
self.v1
.iter()
.zip(self.v2.iter())
.zip(self.use_v1.iter())
.map(|((v1, v2), use_v1)| if use_v1 { v1 } else { v2 })
}
}
This will iterate over all three Vec (actually the shortest of them) and return either from v1 or v2.
Notice that I switched use_v1 from a Vec<T> to a Vec<bool>, which seems to be what you have, given the way you use it.
This is a simplified example of my code:
#[derive(Debug, Clone, Copy)]
enum Data<'a> {
I32(&'a [i32]),
F64(&'a [f64]),
}
impl<'a> From<&'a [i32]> for Data<'a> {
fn from(v: &'a [i32]) -> Data<'a> {
Data::I32(v)
}
}
impl<'a> From<&'a [f64]> for Data<'a> {
fn from(v: &'a [f64]) -> Data<'a> {
Data::F64(v)
}
}
#[derive(Debug, Clone, Copy)]
struct DataVar<'a> {
name: &'a str,
data: Data<'a>,
}
impl<'a> DataVar<'a> {
fn new<T>(name: &'a str, data: T) -> Self
where
T: Into<Data<'a>>,
{
Self {
name,
data: data.into(),
}
}
}
First of all, considering that I need to cast different DataVars to the same vector, and I would like to avoid using trait objects, do you think my implementation is correct or do you have suggestions for improvement?
Now my main question. I can define new DataVars passing a slice, for instance as follows:
let x = [1, 2, 3];
let xvar = DataVar::new("x", &x[..]);
How can I modify my constructor so that it works not only with a slice, but also with a reference to array or vector? For instance I would like the following to work as well:
let x = [1, 2, 3];
let xvar = DataVar::new("x", &x);
EDIT:
Now I tried implementing the same code using a trait object instead of an enum, but the result is even worse... isn't there really any solution to this?
trait Data: std::fmt::Debug {}
impl Data for &[i32] {}
impl Data for &[f64] {}
#[derive(Debug, Clone, Copy)]
struct DataVar<'a> {
name: &'a str,
data: &'a dyn Data,
}
impl<'a> DataVar<'a> {
fn new<T>(name: &'a str, data: &'a T) -> Self
where
T: Data,
{
Self { name, data }
}
}
let x = [1, 2, 3];
let xvar = DataVar::new("x", &&x[..]);
To me, AsRef doesn't seem to be the right abstraction for two reasons: first, because it's possible (if unlikely) for a type to implement both AsRef<[i32]> and AsRef<[f64]>, and it's not clear what should happen in that case; and second, because there's already a built-in language feature (coercion) that can turn Vec<T> or &[T; n] into &[T], and you're not taking advantage of it.
What I'd like is to write a new function that looks basically like this:
fn new<T>(name: &'a str, data: &'a [T]) -> Self
where
// what goes here?
This will automatically work with &[T; n], &Vec<T>, &Cow<T>, etc. if we can tell the compiler what to do with T. It makes sense that you could make a trait that knows how to convert &'a [Self] to Data and is implemented for i32 and f64, so let's do that:
trait Item: Sized {
fn into_data<'a>(v: &'a [Self]) -> Data<'a>;
}
impl Item for i32 {
fn into_data<'a>(v: &'a [i32]) -> Data<'a> {
Data::I32(v)
}
}
impl Item for f64 {
fn into_data<'a>(v: &'a [f64]) -> Data<'a> {
Data::F64(v)
}
}
The trait bound on new becomes trivial:
impl<'a> DataVar<'a> {
fn new<T>(name: &'a str, data: &'a [T]) -> Self
where
T: Item,
{
Self {
name,
data: T::into_data(data),
}
}
}
I find this more readable than the version with From and AsRef, but if you still want From, you can easily add it with a generic impl:
impl<'a, T> From<&'a [T]> for Data<'a>
where
T: Item,
{
fn from(v: &'a [T]) -> Self {
T::into_data(v)
}
}
We can use the AsRef trait to convert references to arrays or vectors to slices. AsRef is a generic trait, so we need to introduce a second type parameter to represent the "intermediate type" (the slice type). After calling as_ref, we've got a slice that can be converted to a Data using into.
impl<'a> DataVar<'a> {
fn new<T, U>(name: &'a str, data: &'a T) -> Self
where
T: AsRef<U> + ?Sized,
U: ?Sized + 'a,
&'a U: Into<Data<'a>>,
{
Self {
name,
data: data.as_ref().into(),
}
}
}
Note however that the data parameter is now a reference: this is necessary because the lifetime of the reference returned by as_ref is bound by the lifetime of the self parameter passed to as_ref. If we changed the parameter back to data: T, then data.as_ref() now implicitly references data in order to call as_ref, which expects a shared reference to self (&self). But data here is a local parameter, which means that the lifetime of the reference created by this implicit referencing operation is limited to the local function, and so is the reference returned by data.as_ref(). This lifetime is shorter than 'a, so we can't store it in the DataVar and return it.
If you need to handle data values that are not references in addition to values that are references, this solution cannot support that, unfortunately.
This is actually the best solution for my case:
impl<'a> DataVar<'a> {
fn new<T, U>(name: &'a str, data: &'a T) -> Self
where
T: AsRef<[U]> + ?Sized,
U: 'a,
&'a [U]: Into<Data<'a>>,
{
Self {
name,
data: data.as_ref().into(),
}
}
}
It works with slices, references to vectors, and references to arrays up to length 32 which implement AsRef<[T]> https://doc.rust-lang.org/beta/std/convert/trait.AsRef.html
Thanks #Francis for your hints!
Actually, this is IMHO the best solution... so similar to my initial code, I just needed a small fix in the new constructor:
#[derive(Debug, Clone, Copy)]
enum Data<'a> {
I32(&'a [i32]),
F64(&'a [f64]),
}
impl<'a> From<&'a [i32]> for Data<'a> {
fn from(data: &'a [i32]) -> Data<'a> {
Data::I32(data)
}
}
impl<'a> From<&'a [f64]> for Data<'a> {
fn from(data: &'a [f64]) -> Data<'a> {
Data::F64(data)
}
}
#[derive(Debug, Clone, Copy)]
struct DataVar<'a> {
name: &'a str,
data: Data<'a>,
}
impl<'a> DataVar<'a> {
fn new<T>(name: &'a str, data: &'a [T]) -> Self
where
&'a [T]: Into<Data<'a>>,
{
Self {
name,
data: data.into(),
}
}
}
#trentcl your solution is brilliant! Now I see how to leverage coercion.
However I tweaked it a little bit as follows, I will finally use this code unless you see any drawbacks in it, thanks!
#[derive(Debug, Clone, Copy)]
enum Data<'a> {
I32(&'a [i32]),
F64(&'a [f64]),
}
trait IntoData<'a>: Sized {
fn into_data(&self) -> Data<'a>;
}
impl<'a> IntoData<'a> for &'a [i32] {
fn into_data(&self) -> Data<'a> {
Data::I32(&self)
}
}
impl<'a> IntoData<'a> for &'a [f64] {
fn into_data(&self) -> Data<'a> {
Data::F64(&self)
}
}
#[derive(Debug, Clone, Copy)]
struct DataVar<'a> {
name: &'a str,
data: Data<'a>,
}
impl<'a> DataVar<'a> {
fn new<T>(name: &'a str, data: &'a [T]) -> Self
where
&'a [T]: IntoData<'a>,
{
Self {
name,
data: data.into_data(),
}
}
}
LimitedFifoQueue is a struct that wraps the functionality of a VecDeque to limit the number of items it will store at any time:
use std::collections::{vec_deque, VecDeque};
use std::fmt;
use std;
#[derive(Debug)]
pub struct LimitedFifoQueue<T> {
size: usize,
store: VecDeque<T>,
}
impl<T> LimitedFifoQueue<T> where T: fmt::Display {
pub fn new(size: usize) -> LimitedFifoQueue<T> {
LimitedFifoQueue {
size: size,
store: VecDeque::with_capacity(size),
}
}
pub fn push(&mut self, elem: T) {
self.store.push_front(elem);
if self.store.len() > self.size {
self.store.pop_back();
}
}
pub fn clear(&mut self) {
self.store.clear();
}
}
I've implemented the IntoIterator trait as follows:
impl<T> IntoIterator for LimitedFifoQueue<T> where T: fmt::Display {
type Item = T;
type IntoIter = vec_deque::IntoIter<T>;
fn into_iter(self) -> Self::IntoIter {
self.store.into_iter()
}
}
And a simplified function that loops through and prints each Item:
fn print_all<I>(lines: &I) where I: IntoIterator {
for string in lines.into_iter() {
println!("{}", string);
}
}
This gives me the following error:
println!("{}", string);
^^^^^^ the trait `std::fmt::Display` is not implemented for `<I as std::iter::IntoIterator>::Item`
I have created a playground of the code with a full stack trace here.
Also, I'm aware that there may be a better way to accomplish what I'm trying to do. I'd love to hear any additional suggestions.
How can I implement std::fmt::Display for a custom IntoIterator::Item?
You cannot. Item might be a type you don't own, and Display is a trait you don't own. You cannot implement a trait you don't own for a type you don't own.
All you can do is require that Item implements Display:
fn print_all<I>(lines: I)
where I: IntoIterator,
I::Item: fmt::Display,
{
for string in lines.into_iter() {
println!("{}", string);
}
}
You don't need any of the other T: Display bounds on your data structure or its methods, as none of those implementations care to print out a value.
Incidentally, into_iter is automatically called on the for-loops argument, so you only need to say:
fn print_all<I>(lines: I)
where I: IntoIterator,
I::Item: fmt::Display,
{
for string in lines {
println!("{}", string);
}
}
You may also wish to review How to implement Iterator and IntoIterator for a simple struct?, as you are passing &lfq into print_all, but &LimitedFifoQueue doesn't implement IntoIterator, only LimitedFifoQueue does. These are different types. You'll need something like
impl<'a, T> IntoIterator for &'a LimitedFifoQueue<T> {
type Item = &'a T;
type IntoIter = vec_deque::Iter<'a, T>;
fn into_iter(self) -> Self::IntoIter {
self.store.iter()
}
}
This problem has nothing to do with your IntoIterator implementation or the rest of your type definition. Just take a look at this code:
fn print_all<I>(lines: &I) where I: IntoIterator {
for string in lines.into_iter() {
println!("{}", string);
}
}
This piece of code doesn't even know about your LimitedFifoQueue type! It takes a value of generic type I. What do we know about I? It implements IntoIterator. Great, what does that tell us about the values the iterator will spit out? Nothing!
So it could be anything, in particular also stuff that doesn't implement fmt::Display. So what we want to do is to annotate that the items of the iterator should at least implement fmt::Display. How is that done? By adding a bound to the associated type Item of the IntoIterator trait:
fn print_all<I>(lines: &I)
where I: IntoIterator,
I::Item: fmt::Display,
{ ... }
Once you understand that you can also add bounds to associated items this makes intuitive sense.
After you fixed that error, another error will be reported, about "moving out of borrowed content". This is a fairly standard error, which I won't explain in detail here. But in summary: your print_all() function should receive a I instead of a &I.
pub struct Storage<T>{
vec: Vec<T>
}
impl<T: Clone> Storage<T>{
pub fn new() -> Storage<T>{
Storage{vec: Vec::new()}
}
pub fn get<'r>(&'r self, h: &Handle<T>)-> &'r T{
let index = h.id;
&self.vec[index]
}
pub fn set(&mut self, h: &Handle<T>, t: T){
let index = h.id;
self.vec[index] = t;
}
pub fn create(&mut self, t: T) -> Handle<T>{
self.vec.push(t);
Handle{id: self.vec.len()-1}
}
}
struct Handle<T>{
id: uint
}
I am currently trying to create a handle system in Rust and I have some problems. The code above is a simple example of what I want to achieve.
The code works but has one weakness.
let mut s1 = Storage<uint>::new();
let mut s2 = Storage<uint>::new();
let handle1 = s1.create(5);
s1.get(handle1); // works
s2.get(handle1); // unsafe
I would like to associate a handle with a specific storage like this
//Pseudo code
struct Handle<T>{
id: uint,
storage: &Storage<T>
}
impl<T> Handle<T>{
pub fn get(&self) -> &T;
}
The problem is that Rust doesn't allow this. If I would do that and create a handle with the reference of a Storage I wouldn't be allowed to mutate the Storage anymore.
I could implement something similar with a channel but then I would have to clone T every time.
How would I express this in Rust?
The simplest way to model this is to use a phantom type parameter on Storage which acts as a unique ID, like so:
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
}
This solves your problem in the simplest case, but has some downsides. Mainly, it depends on the use to define and use all of these different phantom types and to prove that they are unique. It doesn't prevent bad behavior on the user's part where they can use the same phantom type for multiple Storage instances. In today's Rust, however, this is the best we can do.
An alternative solution that doesn't work today for reasons I'll get in to later, but might work later, uses lifetimes as anonymous id types. This code uses the InvariantLifetime marker, which removes all sub typing relationships with other lifetimes for the lifetime it uses.
Here is the same system, rewritten to use InvariantLifetime instead of 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);
}
In a hypothetical future, the assignment of lifetimes may change and we may grow a better mechanism for this sort of tagging. However, for now, the best way to accomplish this is with phantom types.