These are chat archives for rust-lang/rust

19th
Jun 2017
NicolasVidal
@NicolasVidal
Jun 19 2017 11:28
Hey there, I've got a simple question about passing pure functions (no side effect, no captured variables) as parameters and am a bit confused about what type to use ...
let's size I want to pass the 'add' function, should I do it like that ? :
fn do_operation(func: &Fn(i32, i32) -> i32) -> i32 {
    (*func)(42, 68)
}
or like that ? :
fn do_operation2(func: &fn(i32, i32) -> i32) -> i32 {
    (*func)(42, 68)
}
with the second one, I can't call it with a closure
NicolasVidal
@NicolasVidal
Jun 19 2017 11:34
So I would be inclined to use the first signature, as I can use it indiferently with functions and with closures
but is there a cost penalty for doing that ?
lastly is there something in between a real function and a closure ? Like an anonymous pure function syntax ?
Michal 'vorner' Vaner
@vorner
Jun 19 2017 11:37
@NicolasVidal: The most common use is:
fn do_operation<F: Fn(i32, i32) -> i32>(f: &F) -> i32 {
  (*f)(42, 68)
}
That one is without a cost and works for both cases.
NicolasVidal
@NicolasVidal
Jun 19 2017 11:38
actually my problem is passing an array of functions
Michal 'vorner' Vaner
@vorner
Jun 19 2017 11:38
Your first one has small overhead due to use of dynamic dispatch/a trait object.
Anton Ugryumov
@dvec
Jun 19 2017 11:39
You can use the first variant with closures: https://is.gd/E4Iu5g
NicolasVidal
@NicolasVidal
Jun 19 2017 11:40
yes @AUgryumov it's what I said :)
@vorner won't the generic version be problematic with an array of functions ?
Michal 'vorner' Vaner
@vorner
Jun 19 2017 11:41
Possibly, it may be.
Each closure (if they would be closures) would have a different type, so creating an array of these wouldn't work.
NicolasVidal
@NicolasVidal
Jun 19 2017 11:42
even if they have the same signature ?
Anton Ugryumov
@dvec
Jun 19 2017 11:42
Why do you need to make generic function?
NicolasVidal
@NicolasVidal
Jun 19 2017 11:43
I'm a bit confused on where the closures are allocated
Michal 'vorner' Vaner
@vorner
Jun 19 2017 11:43
Functions would all have the same type, but closures carry their environment with them. They are basically a function + a struct with variables. So yes, they'd be different types.
NicolasVidal
@NicolasVidal
Jun 19 2017 11:44
@vorner is it possible to define an anonymous function ? i.e. like the closure syntax, but without all the environment with it ?
Michal 'vorner' Vaner
@vorner
Jun 19 2017 11:46
If you don't touch any of the variables in the environment, they don't get bundled. So you get a function + empty struct.
Or you can have a local (but not anonymous) function.
NicolasVidal
@NicolasVidal
Jun 19 2017 11:46
but still different types ?
Michal 'vorner' Vaner
@vorner
Jun 19 2017 11:46
It differs only in the scope where it is visible
Still different types, because they'd be separate independent empty structs.
NicolasVidal
@NicolasVidal
Jun 19 2017 11:47
it seems we can't get the best of both worlds ^^ :)
Michal 'vorner' Vaner
@vorner
Jun 19 2017 11:47
This might be what you want:
fn outern() {
  fn inner() {
    unimplemented!();
  }
}
Such thing is an „ordinary“ function
but it lives inside the scope and is not visible from outside
NicolasVidal
@NicolasVidal
Jun 19 2017 11:49

but we must do dmth like that ? :

fn add_tmp(a:i32, b:i32) -> i32 {
        a + b
    }
    do_operation(&add_tmp);

can't we use it on the same line we declare it ?

Michal 'vorner' Vaner
@vorner
Jun 19 2017 11:50
I'm afraid not. But I don't know everything.
NicolasVidal
@NicolasVidal
Jun 19 2017 11:51
ok, thank you very much for your answers @vorner , last thing you said my first version was using trait objects, does it mean it is allocated on the heap ?
Michal 'vorner' Vaner
@vorner
Jun 19 2017 11:54
No, unless you put them into a Box or something.
You're passing a reference to the closure allocated on stack.
NicolasVidal
@NicolasVidal
Jun 19 2017 11:55
ok perfect, so it's not too much of a loss maybe ...
Denis Lisov
@tanriol
Jun 19 2017 11:56
There's an accepted RFC to allow coercing non-capturing closures into function pointers.
Michal 'vorner' Vaner
@vorner
Jun 19 2017 11:57
Only that dynamic dispatch. Which might get optimized out.
NicolasVidal
@NicolasVidal
Jun 19 2017 11:58
@tanriol thanks ! that's right on spot with my issue
Denis Lisov
@tanriol
Jun 19 2017 12:00
Stabilized in rust-lang/rust@5d2512e for 1.19, available in beta now (play)
NicolasVidal
@NicolasVidal
Jun 19 2017 13:00

ok, so as I understand, in rust stable between :

fn do_operation_on_four_functions<
    F1 : Fn(i32, i32)-> i32 ,
    F2 : Fn(i32, i32)-> i32 ,
    F3 : Fn(i32, i32)-> i32 ,
    F4 : Fn(i32, i32)-> i32>(f1:F1,
                             f2:F2,
                             f3:F3,
                             f4:F4,) -> i32 {
    f1(42, 68) + f2(42, 68) + f3(42, 68) + f4(42, 68)
}

fn do_operation_on_four_functions2(functions: [&Fn(i32, i32) -> i32; 4]) -> i32 {
    (functions[0])(42, 68) + (functions[1])(42, 68) + (functions[2])(42, 68) + (functions[3])(42, 68)
}

I should prefer the first one performance wise despite of the awful syntax

Denis Lisov
@tanriol
Jun 19 2017 13:03
Well, I'd just use beta for now... it's just a few weeks.
NicolasVidal
@NicolasVidal
Jun 19 2017 13:03
and then in 1.19 I could switch to fn
I'm not already familiar with the release cycle times
if it's in a few weeks, then it's a go ! :)
Denis Lisov
@tanriol
Jun 19 2017 13:04
The next release is coming in a month, on the 20th of July.
NicolasVidal
@NicolasVidal
Jun 19 2017 13:05
Anyway, thank you again for you explanations, links and answers @tanriol , @vorner and @AUgryumov !