Skip to content

Instantly share code, notes, and snippets.

@xiaoniu-578fa6bff964d005
Created October 22, 2018 13:57
Show Gist options
  • Save xiaoniu-578fa6bff964d005/e5c8a70080dc881cbd6ae838993825bb to your computer and use it in GitHub Desktop.
Save xiaoniu-578fa6bff964d005/e5c8a70080dc881cbd6ae838993825bb to your computer and use it in GitHub Desktop.
Is it possible to unchecked unwrap Option with safety?
mod approach5 {
mod variance {
use std::marker::PhantomData;
use std::fmt;
use std::hash::{Hash, Hasher};
use std::cmp::Ordering;
/// A marker for forcing T to be considered invariant.
// *mut T is invariant in T
pub struct Invariant<T>(PhantomData<*mut T>);
impl<T> Invariant<T> {
/// Create a new Invariant marker instance.
///
/// All instances of Invariant with the same T are equivalent.
#[inline]
pub fn new() -> Self { Invariant(PhantomData) }
}
impl<T> Default for Invariant<T> {
#[inline]
fn default() -> Self { Invariant::new() }
}
impl<T> Copy for Invariant<T> {}
impl<T> Clone for Invariant<T> {
#[inline]
fn clone(&self) -> Self { Invariant::new() }
}
impl<T> fmt::Debug for Invariant<T> {
#[inline]
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.write_str("Invariant Type Marker")
}
}
impl<T> PartialEq<Invariant<T>> for Invariant<T> {
#[inline]
fn eq(&self, _: &Self) -> bool { true }
#[inline]
fn ne(&self, _: &Self) -> bool { false }
}
impl<T> PartialOrd<Invariant<T>> for Invariant<T> {
#[inline]
fn partial_cmp(&self, _: &Self) -> Option<Ordering> {
Some(Ordering::Equal)
}
#[inline]
fn lt(&self, _: &Self) -> bool { false }
#[inline]
fn le(&self, _: &Self) -> bool { true }
#[inline]
fn gt(&self, _: &Self) -> bool { false }
#[inline]
fn ge(&self, _: &Self) -> bool { true }
}
impl<T> Eq for Invariant<T> {}
impl<T> Ord for Invariant<T> {
#[inline]
fn cmp(&self, _: &Self) -> Ordering { Ordering::Equal }
}
impl<T> Hash for Invariant<T> {
#[inline]
fn hash<H: Hasher>(&self, state: &mut H) {
().hash(state)
}
}
}
#[macro_use]
mod fixobj {
// extern crate variance;
// use self::variance::Invariant;
use super::variance::Invariant;
/// solidify the lifetime.
pub struct FixRange<'a,T: 'a> (Invariant<&'a T>);
impl<'a,T: 'a> FixRange<'a, T> {
pub fn new(_: &'a &'a T) -> Self {
FixRange(Invariant::new())
}
}
impl<'a, T> Copy for FixRange<'a, T> {}
impl<'a, T> Clone for FixRange<'a, T> {
#[inline]
fn clone(&self) -> Self { FixRange(Invariant::new()) }
}
/// ensure that value don't change in lifetiem 'a
pub struct FixObj<'a,T: 'a> (&'a T, FixRange<'a, T>);
impl<'a,T: 'a> FixObj<'a, T> {
pub fn new(c: &'a &'a T) -> Self { FixObj(c, FixRange::new(c)) }
pub fn get<'b>(&'b self) -> &'b T where 'a: 'b{ self.0 }
pub fn get_range<'b>(&'b self) -> FixRange<'a, T> { self.1 }
}
impl<'a, T> Copy for FixObj<'a, T> {}
impl<'a, T> Clone for FixObj<'a, T> {
#[inline]
fn clone(&self) -> Self { FixObj(self.0,self.1) }
}
#[macro_export]
macro_rules! fix { ($x:ident, $f:ident) => (let reference = &$x; let $f = FixObj::new(&reference);) }
}
mod prechecked_option {
use super::fixobj::*;
/// T should be an Option<U>
/// In the range of Fix<'a, T>, guarantee the T is not empty.
#[derive(Clone, Copy)]
pub struct OptionNotNonProof<'a, T: 'a> (FixRange<'a, T>);
pub fn check<'a, U: 'a>(fx: FixObj<'a, Option<U>>) -> Option<OptionNotNonProof<'a, Option<U>>> {
match fx.get() {
Some(_) => Some(OptionNotNonProof(fx.get_range())),
None => None,
}
}
pub fn prechecked_unwrap<'a: 'b, 'b, U>(fx: &'b FixObj<'a, Option<U>>, proof: OptionNotNonProof<'a, Option<U>>) -> &'b U {
match fx.get() {
Some(x) => x,
None => unreachable!(),
}
}
}
pub fn test() {
test1();
test2();
}
fn test1() {
println!("test FixObj.");
use self::fixobj::*;
let a: u8 = 1;
/// one liner to define fixobj.
fix!(a,fa);
/// another way to define it.
let ra = &a;
let mut fa = FixObj::new(&ra);
let ra2 = &a;
let mut fa2 = FixObj::new(&ra2);
// both won't compile
// fa2 = fa;
// fa = fa2;
/// However can be crack with group bind.
let (a,b) = (42 as u8, 1 as u8);
let (ra, rb) = (&a, &b);
let (mut fa, mut fb) = (FixObj::new(&ra),FixObj::new(&rb));
fa = fb;
// fix about x cannot be forged with &x.
// fn fix<'a: 'b, 'b, T: 'a>(x: &'a T) -> FixObj<'b, T> { FixObj::new(&x) }
}
fn test2() {
println!("Prechecked Unwrap.");
use std::mem;
use self::fixobj::*;
use self::prechecked_option::*;
let b: Option<u8> = None;
fix!(b,fb);
let a: Option<u8> = Some(42);
fix!(a,fa);
let z = check(fa);
if let Some(proof) = z {
println!("Got proof.");
println!("proof size: {}", mem::size_of_val(&proof));
// Do something...
println!("unwrap with proof: {}", prechecked_unwrap(&fa,proof));
// The proof is for fa only, and cannot be used for fb.
// println!("unwrap with proof: {}", prechecked_unwrap(&fb,proof));
} else {
println!("No proof.");
}
// Not possible to forge proof.
// field is private.
// let proof = OptionNotNonProof{0:fa.get_range()};
/// Crack with group bind.
let (a,b)=(Some(42),None);
let (ra, rb) = (&a, &b);
let (mut fa, mut fb) = (FixObj::new(&ra),FixObj::new(&rb));
let z = check(fa);
if let Some(proof) = z {
println!("Got proof.");
println!("unwrap with proof: {}", prechecked_unwrap(&fb,proof));
} else {
println!("No proof.");
}
}
// extern crate variance;
// use self::variance::InvariantLifetime;
// use std::marker::PhantomData;
// use self::variance::Invariant;
}
fn main() {
use approach5::*;
test();
println!("Hello, world!");
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment