2

I'm learning Rust and have encountered a vexing issue involving closures. I've gotten a lot of the basic closure examples to work, but now that I've stepped out of the basic "make adder" and "call on" examples, things are getting messy. This is the first part of my code, which works:

trait TransformationElt<T, F> where F: Fn(T) -> T {
    fn get_transform(&self) -> Box<F>;
}

pub struct AddSome { pub x: i64 }

impl AddSome  {
    fn the_transform(&self) -> Box<Fn(i64) -> i64> {
        Box::new(|x: i64| x + 1 as i64)
    }
}

This successfully returns a heap-allocated closure that adds 1, implementing our AddSome type. I want AddSome to implement TransformationElt, specifically for the type i64:

impl<F: Fn(i64) -> i64> TransformationElt<i64, F> for AddSome  {
    fn get_transform(&self) -> Box<F> {
        Box::new(move |x: i64| x + self.x as i64)
    }
}

After much hackery and trying different things, I still can't get it to compile. The error I typically get is:

src/lex/math/algebra/groups.rs:31:16: 31:46 error: mismatched types: expected F, found [closure@src/lex/math/algebra/groups.rs:31:16: 31:46 self:_] (expected type parameter, found closure) [E0308]

How do I get around this seemingly basic issue and implement my "transform" type?

One final thing -- I have reasons I want to keep things as closures. I intend to compose them, etc. etc., things where I really do need higher-order functions.

Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
William
  • 337
  • 3
  • 10

1 Answers1

2

The compiler is stopping you because you are trying to lie to it. It's pretty good at seeing through us programmers when we lie.

How did you lie? You said "I'm going to write a method that takes some arbitrary type (with some restrictions) and then I'm going return a box containing only that type". You then proceeded to not return a box of the passed in value; you inserted wrapper junk, as far as the compiler cares.

Here's a small example:

fn wrapper<F>(f: F) -> Box<F>
where
    F: Fn(u8) -> u8,
{
    Box::new(|x| f(x) + 1)
}

What you really want to say is that you will return something that implements the trait, but without describing how (because you can't specify a closure). You do this with indirection, a boxed trait object:

fn wrapper<F>(f: F) -> Box<Fn(u8) -> u8>
where
    F: Fn(u8) -> u8 + 'static,
{
    Box::new(move |x| f(x) + 1)
}

See also:

  1. What is the correct way to return an Iterator (or any other trait)?
  2. Return a closure from a function
  3. Returning a closure from a function
  4. Figuring out return type of closure
Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366