Sized and lambdas

Hello all,

In the vein of another topic, I place my code here
https://play.rust-lang.org/
My question is at the end

use std::ops::Add;

type Sound = f64;

struct Ugen<T> {
    time: f64,
    handler: T,
}
impl<T: SoundGen> Ugen<T> {
    pub fn new(handler: T) -> Self {
        Ugen { time: 0.0, handler }
    }
}

impl<T> Iterator for Ugen<T>
    where T : SoundGen
{
    type Item = Sound;
    
    fn next(&mut self) -> Option<Self::Item> {
        self.time += 0.1;
        Some(self.handler.gen(self.time))
    }
}

trait SoundGen {
    fn gen(&mut self, f: f64) -> Sound;
}

impl<F: Fn(f64) -> Sound> SoundGen for F {
    fn gen(&mut self, f: f64) -> Sound {
        self(f)
    }
}

impl<T: SoundGen> SoundGen for Ugen<T> {
    fn gen(&mut self, _f: f64) -> Sound {
        self.time += 0.1;
        self.handler.gen(self.time)
    }
}

struct Sum<A, B> {
    a: A,
    b: B,
}

impl<A: SoundGen, B: SoundGen> SoundGen for Sum<A, B> {
    fn gen(&mut self, val: f64) -> Sound {
        self.a.gen(val) + self.b.gen(val)
    }
}

impl<A: SoundGen, B: SoundGen> Add<Ugen<B>> for Ugen<A> {
    type Output = Ugen<Sum<Self, Ugen<B>>>;
    
    fn add(self, other: Ugen<B>) -> Self::Output {
        Ugen::new(Sum {
            a: self,
            b: other
        })
    }
}

#[cfg(test)]
mod tests{
    use super::Ugen;
    use super::Sound;
    use super::SoundGen;

    macro_rules! init {
        () => (
        (
            Ugen::new(|time:Sound| time.sin()),
            Ugen::new(|_time| { 4.0 }))
        );
    }
    
    
    //// HERE   !!!!!!!!!!!!!!!!!!!!!
    //fn init() -> (Ugen<dyn SoundGen + Sized>, Ugen<dyn SoundGen + Sized>)
    //{
    //    let a = Ugen::new(|time:Sound| {
    //        time.sin()
    //    });
    //    let b = Ugen::new(|_time| { 4.0 });
    //    (a, b)
    //}
    ////////////////////////////////
    
    
    #[test]
    fn first() {
        let (a, b) = init!();
        let mut c = a + b;
        let d = c.next().unwrap();
        assert!(d == 4.0 + 0.1f64.sin(), format!("{}", d))
    }
    #[test]
    fn multiple() {
        let a = Ugen::new(|time:Sound| {
            time.sin()
        });
        let b = Ugen::new(|_time| { 4.0 });
        let z = Ugen::new(|_time| { 4.0 });
        let mut d = a + b + z;
        let e1 = d.next().unwrap(); 
        let e2 = d.next().unwrap(); 
        assert!(e1 == 8.0 + 0.1f64.sin(), format!("{}", e1));
        assert!(e2 == 8.0 + 0.2f64.sin(), format!("{}", e2));
    }
}

To be able to call my Ugen I use a macro, because I was unable to create a function. All is about the dynamic size of my lambdas, which I don't know how to precise. Maybe add a Sized trait, but I was unfortunate to succed :confused:

Thanks

It sounds like you want the impl trait feature:

fn init() -> (Ugen<impl SoundGen>, Ugen<impl SoundGen>) {
    let a = Ugen::new(|time:Sound| {
        time.sin()
    });
    let b = Ugen::new(|_time| { 4.0 });
    (a, b)
}

The difference between dyn Trait and impl Trait is:

  1. dyn Trait means “This could be any type that implements Trait and it doesn't always have to be the same one”
  2. impl Trait means “This is a specific type that implements Trait. I haven't told you which one, but I guarantee it'll always be the same one”

The advantage of impl Trait is that when compiling, the compiler figures out which specific type it's actually using, and therefore the compiler knows the size of the type, allowing you to use it without a box.

2 Likes

You can have a F : FnMut(u64) -> Sound bound instead, which is less restrictive than Fn(u64) -> Sound (e.g., it will allow closures that mutate some state (without requiring Interior / Aliased Mutability)).

Minor nitpick: impl Trait in return position (clarification: when used as a function parameter / argument it means something else).

1 Like

Great. Thanks :wink: