Reputation: 402
I'm trying to add a function pointer to a struct, and can't figure out how to do it. Here is a simple example:
struct Ure<'a> {
num: u64,
func: Option<&'a Fn(u64) -> u64>,
}
impl<'a> Ure<'a> {
fn g42_ure(n: u64) -> Ure<'a> {
Ure {
num: n,
func: Some(&Ure::g42),
}
}
fn g42(u: u64) -> u64 {
if u > 42 { u } else { 42 }
}
}
This leads to the following error:
error: borrowed value does not live long enough
--> <anon>:10:23
|
10 | func: Some(&Ure::g42),
| ^^^^^^^^ does not live long enough
11 | }
12 | }
| - temporary value only lives until here
|
note: borrowed value must be valid for the lifetime 'a as defined on the body at 7:34...
--> <anon>:7:35
|
7 | fn g42_ure(n: u64) -> Ure<'a> {
| ___________________________________^ starting here...
8 | | Ure {
9 | | num: n,
10 | | func: Some(&Ure::g42),
11 | | }
12 | | }
| |_____^ ...ending here
Is there a way to get a reference to function g42
in this example to live long enough without passing it as an argument to g42_ure
? It does not matter to me where g42
is defined (whether it is inside impl Ure
, g42_ure()
or neither), but coming from an OOP background it seems neater inside impl Ure
.
I'm just learning Rust (and very much enjoying it), so any help would be appreciated, thanks.
Upvotes: 0
Views: 313
Reputation: 88576
The type Fn(u64) -> u64
is not a function pointer, but a trait (-object). Function pointers in Rust are written fn(u64) -> u64
, with a lowercase f
! Also note that fn(u64) -> u64
is already a function pointer, no need to say &fn(u64) -> u64
!
So one way to make it work is by using function pointers (Playground):
struct Ure {
num: u64,
func: Option<fn(u64) -> u64>,
// ^^^^^^^^^^^^^^
}
impl Ure {
fn g42_ure(n: u64) -> Ure {
Ure {
num: n,
func: Some(Ure::g42),
// ^^^^^^^^
}
}
// ...
}
Function pointers have limitations, though. Specifically, they can't have environments like closures have. That's there the Fn
trait comes in which (together with its siblings FnMut
and FnOnce
) abstracts over callable things in general (including function pointers and closures).
The problem is that you can't use trait objects that easily. Probably the easiest is to use a Box
to store and own a trait object on the heap. It would look like this (Playground):
struct Ure {
num: u64,
func: Option<Box<Fn(u64) -> u64>>,
// ^^^^^^^^^^^^^^^^^^^
}
impl Ure {
fn g42_ure(n: u64) -> Ure {
Ure {
num: n,
func: Some(Box::new(Ure::g42)),
// ^^^^^^^^^^^^^^^^^^
}
}
// ...
}
Upvotes: 4