Lazy type with!


#1

Can we have lazy type like this: fn foo(a1 : int, a2 : !int) -> !int { } where ! denote lazy


#2

Could you please elaborate? I’m not sure what you mean to achieve. (Note: I only know c# has lazy types, not what they are or how they work)


#3

Do you mean something like this:

use std::cell::UnsafeCell;
use std::ops::{Deref, DerefMut};
use std::thunk::Invoke;

enum LazyInner<'a, T> {
    Uninitialized(Option<Box<Invoke<(), T> + 'a>>),
    Initialized(T),
}

pub struct Lazy<'a, T> {
    inner: UnsafeCell<LazyInner<'a, T>>,
}

impl<'a, T> Lazy<'a, T> {
    pub fn new<F: 'a>(f: F) -> Self where F: FnOnce() -> T {
        Lazy {
            inner: UnsafeCell::new(LazyInner::Uninitialized(Some(Box::new(move |()|f()))))
        }
    }
}

impl<'a, T> Deref for Lazy<'a, T> {
    type Target = T;
    fn deref(&self) -> &T {
        use LazyInner::*;
        unsafe {
            match *self.inner.get() {
                Uninitialized(ref mut f) => match f.take() {
                    Some(f) => {
                        *self.inner.get() = Initialized(f.invoke(()));
                        &self
                    },
                    None => unreachable!(),
                },
                Initialized(ref value) => value,
            }
        }
    }
}

impl<'a, T> DerefMut for Lazy<'a, T> {
    fn deref_mut(&mut self) -> &mut T {
        use LazyInner::*;
        unsafe {
            match *self.inner.get() {
                Uninitialized(ref mut f) => match f.take() {
                    Some(f) => {
                        *self.inner.get() = Initialized(f.invoke(()));
                        &mut *self
                    },
                    None => unreachable!(),
                },
                Initialized(ref mut value) => value,
            }
        }
    }
}

macro_rules! lazy {
    ($($s:stmt);+) => {{
        Lazy::new(|| { $($s);+ })
    }};
}

macro_rules! movable_lazy {
    ($($s:stmt);+) => {{
        Lazy::new(move || { $($s);+ })
    }};
}

fn test(value: &Lazy<u8>) {
    println!("{}", **value);
}

fn main() {
    let l = lazy! {
        println!("computing");
        1u8 + 2 + 3 + 4
    };
    test(&l);
    test(&l);
}

I doubt this functionality will ever be included in rust itself because it’s a very high level concept and is not a zero-cost abstraction but those macros work fairly well (but could clearly be improved/optimized).


#4

I rarely need lazy values in practice. When I have, FnMut() -> T has been enough, although granted, passing closures around is a little more heavyweight than I’d like it to be (adding a closure means coming up with a name, adding a type parameter, adding a constraint, and then finally adding the value parameter). Other than that, though, I don’t really see a motivation to add more sugar?


#5

There is a lazy library already that provides very close to what you are looking for, https://github.com/reem/rust-lazy.