Function from struct as function pointer?


#1

How to use a function from a struct as a function pointer?

This works well:

fn foo(fx: fn(x: &str)) {
    fx("bar")
}

fn print(x: &str) {
    println!("{:?}", x);
}

fn main() {
    foo(print);
}

Here the same example with a function from a struct:

fn foo(fx: fn(x: &str)) {
    fx("bar")
}

struct remember {
    counter: u32,
}

impl remember {
    fn new() -> remember {
        remember{ counter: 0 }
    }

    fn print_with_count(&mut self, x: &str) {
        self.counter += 1;
        println!("{}: {}", self.counter, x);
    }
}

fn main() {
    let r = remember::new();
    foo(r.print_with_count);
}

Now I get this error message:

src/main.rs:34:15: 34:31 error: attempted to take value of method `print_with_count` on type `remember`
src/main.rs:34     let x = r.print_with_count;
                             ^~~~~~~~~~~~~~~~
src/main.rs:34:15: 34:31 help: maybe a `()` to call it is missing? If not, try an anonymous function
src/main.rs:35:11: 35:27 error: attempted to take value of method `print_with_count` on type `remember`
src/main.rs:35     foo(r.print_with_count);
                         ^~~~~~~~~~~~~~~~
src/main.rs:35:11: 35:27 help: maybe a `()` to call it is missing? If not, try an anonymous function
error: aborting due to 2 previous errors
Could not compile `test`.

Any Ideas? Can I get the struct in an other way?

Thanks for any help.


#2

It can’t work like this - remember::print_with_count is a function with signature fn(&mut remember, &str). The expression r.print_with_count does not result in a value, and in particular doesn’t create a “bound method” as it would e.g. in Python.

You could change foo to take a function of the correct type, and then pass foo(remember::print_with_count). But what you most likely want to do instead is to use a trait, and make foo generic. With trait objects you could then do dynamic dispatch if necessary.


#3

You can also use a lambda: foo(|x| r.print_with_count(x)). However, lambdas can’t be converted to function pointers due to the environment; you need to use FnMut. You can read more about that in the book.


#4

Thanks a lot for the help. According to my real problem, the hint with the trait helped a lot. I read the book before starting with Rust. It looks like I didn’t really understand it :slightly_smiling:

But back on my real problem, I found an other gap. Maybe someone has an idea, how to solve this:

extern crate iron;

use iron::prelude::*;
use iron::middleware::Handler;

struct Remember {
    count: u32,
}

impl Handler for Remember {
    fn handle(&self, _: &mut Request) -> IronResult<Response> {
        self.count += 1; // How to get this mutable?
        let count = format!("{}", self.count);
        let res = Response::with((iron::status::Ok, count));
        Ok(res)
    }
}

fn main() {
    let r = Remember{ count: 0 };
    let c = Chain::new(r);
    Iron::new(c).http("localhost:8080").unwrap();
}

Or maybe I’m running the completely wrong way.

Thanks for any help.


#5

Found a solution, with the persistent plugin for iron. If someone intrested, this currently works:

extern crate iron;
extern crate persistent;

use iron::prelude::*;
use persistent::Write;
use iron::typemap::Key;

struct RememberTest;
impl Key for RememberTest { type Value = Remember; }

struct Remember {
    count: u32,
}

fn handle(req: &mut Request) -> IronResult<Response> {
    let mutex = req.get::<Write<RememberTest>>().unwrap();
    let mut count = mutex.lock().unwrap();
    count.count += 1;
    let count = format!("{}", count.count);
    let res = Response::with((iron::status::Ok, count));
    Ok(res)
}

fn main() {
    let x = Remember{ count: 0 };
    let mut c = Chain::new(handle);
    c.link_before(Write::<RememberTest>::one(x));
    Iron::new(c).http("localhost:8080").unwrap();
}