3

Note that this question pertains to a version of Rust before 1.0 was released

Do I understand correctly that it is now impossible to return a closure from a function, unless it was provided to the function in its arguments? It is very useful approach, for example, when I need the same block of code, parameterized differently, in different parts of program. Currently the compiler does not allow something like this, naturally:

fn make_adder(i: int) -> |int| -> int {
    |j| i + j
}

The closure is allocated on the stack and is freed upon returning from a function, so it is impossible to return it.

Will it be possible to make this work in future? I heard that dynamically-sized types would allow this.

Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
Vladimir Matveev
  • 120,085
  • 34
  • 287
  • 296
  • Okay there was boxed closures syntax ... https://github.com/alco/blog/blob/master/posts/2013-07-22-Rust-lambdas.md#boxed-closures but now I'm not sure how to make the same. Seems like provided in answer solution is the only way. – cnd Jan 15 '14 at 07:27
  • @Heather, yes, these types of closures were removed some time before 0.9. – Vladimir Matveev Jan 15 '14 at 07:42

1 Answers1

3

This can't ever work for a stack closure; it needs to either have no environment or own its environment. The DST proposals do include the possibility of reintroducing a closure type with an owned environment (~Fn), which would satisfy your need, but it is not clear yet whether that will happen or not.

In practice, there are other ways of doing this. For example, you might do this:

pub struct Adder {
    n: int,
}

impl Add<int, int> for Adder {
    #[inline]
    fn add(&self, rhs: &int) -> int {
        self.n + *rhs
    }
}

fn make_adder(i: int) -> Adder {
    Adder {
        n: int,
    }
}

Then, instead of make_adder(3)(4) == 7, it would be make_adder(3) + 4 == 7, or make_adder(3).add(&4) == 7. (That it is Add<int, int> that it is implementing rather than just an impl Adder { fn add(&self, other: int) -> int { self.n + other } is merely to allow you the convenience of the + operator.)

This is a fairly silly example, as the Adder might just as well be an int in all probability, but it has its possibilities.

Let us say that you want to return a counter; you might wish to have it as a function which returns (0, func), the latter element being a function which will return (1, func), &c. But this can be better modelled with an iterator:

use std::num::{Zero, One};

struct Counter<T> {
    value: T,
}

impl<T: Add<T, T> + Zero + One + Clone> Counter<T> {
    fn new() -> Counter<T> {
        Counter { value: Zero::zero() }
    }
}

impl<T: Add<T, T> + Zero + One + Clone> Iterator<T> for Counter<T> {
    #[inline]
    fn next(&mut self) -> Option<T> {
        let mut value = self.value.clone();
        self.value += One::one();
        Some(value)
    }

    // Optional, just for a modicum of efficiency in some places
    #[inline]
    fn size_hint(&self) -> (uint, Option<uint>) {
        (uint::max_value, None)
    }
}

Again, you see the notion of having an object upon which you call a method to mutate its state and return the desired value, rather than creating a new callable. And that's how it is: for the moment, where you might like to be able to call object(), you need to call object.method(). I'm sure you can live with that minor inconvenience that exists just at present.

Chris Morgan
  • 86,207
  • 24
  • 208
  • 215
  • Thank you for thorough explanation. Yes, the structs are the way to do it now (well, closures are poor man objects and vice versa, aren't they?:)), but the advantage of returning closures is very concise syntax and no semantic nor syntactic overhead of additional structs and impls. Hope owned closures will return to us some day :) – Vladimir Matveev Jan 15 '14 at 07:44
  • What if I want to have a function that returns something that I can pass to `map`? I can't seriously define a new iterator type every time as a workaround. – Sebastian Redl Mar 06 '14 at 11:41
  • @SebastianRedl: sorry, but for the moment new iterator types are the only way to do it. – Chris Morgan Mar 10 '14 at 21:22