Using a phantom scope to bypass bounds-checking

Viewed 84

I want to construct a indexing-type associated to a value which is a wrapper around a usize such that any vector created from that value I don't need to bounds-check the index. It seems like phantom lifetimes can be used to do some small amount of dependently typed programming like this. Will this work or are there things I'm not considering?

In other words, using the module below is it impossible to write ("safe") code which will step out of memory?

Also, is there a way to do this without the unit references?

pub mod things {
    use std::iter;

    #[derive(Clone, Copy)]
    pub struct ThingIndex<'scope>(usize, &'scope ());
    pub struct Things {
        nthings: usize,
    }
    pub struct ThingMapping<'scope, V>(Vec<V>, &'scope ());

    impl Things {
        pub fn in_context<F: FnOnce(&Things) -> V, V>(nthings: usize, continuation: F) -> V {
            continuation(&Things { nthings })
        }
        pub fn make_index<'scope>(&'scope self, i: usize) -> ThingIndex<'scope> {
            if i >= self.nthings {
                panic!("Out-of-bounds index!")
            }
            ThingIndex(i, &())
        }
        pub fn make_mapping<'scope, V: Clone>(&'scope self, def: V) -> ThingMapping<'scope, V> {
            ThingMapping(iter::repeat(def).take(self.nthings).collect(), &())
        }
    }

    impl<'scope, V> ThingMapping<'scope, V> {
        pub fn get<'a>(&'a self, ind: ThingIndex<'scope>) -> &'a V {
            unsafe { &self.0.get_unchecked(ind.0) }
        }
        // ...
    }
}

Update:

This doesn't seem to work. I added a test that I expected would fail to compile and it compiled without complaint. Is there a way to repair it and make it work? What if I write a macro?

#[cfg(test)]
mod tests {
    use crate::things::*;

    #[test]
    fn it_fails() {
        Things::in_context(1, |r1| {
            Things::in_context(5, |r2| {
                let m1 = r1.make_mapping(());
                let i2 = r2.make_index(3);
                assert_eq!(*m1.get(i2), ());
            });
        })
    }
}

Note: in_context is loosely based on Haskell's runST function. In Haskell the type-signature of runST requires RankNTypes. I wonder if perhaps this is impossible because the Rust compiler does nothing conjugate to the behavior of RankNTypes.

0 Answers
Related