How to use a Pointer Function with self (in an impl)

Hi,

I use Nickel and I would like to use a method of the implementation as pointer function!

router.add_route(Method::Head, "/api/kv/:key", &self.hello_world);

This is the errors that I get:

error[E0609]: no field `router` on type `&server::Server`
   --> src\server.rs:103:9
    |
103 |         router.add_route(Method::Head, "/api/kv/:key", &self.hello_world);
    |         ^^^^^^

error[E0615]: attempted to take value of method `hello_world` on type `&server::Server`
   --> src\server.rs:103:62
    |
103 |         router.add_route(Method::Head, "/api/kv/:key", &self.hello_world);
    |                                                              ^^^^^^^^^^^ help: use parentheses to call the method: `hello_world(...)`

error: aborting due to 2 previous errors

My question is:

How to use a function pointer in implementation with a pointer function in the same impl ?

Thank you

Function pointer foo is called as (foo)(). If you have a field foo, then it's (self.foo)(). If it's a method, then (self.foo)(self).

2 Likes

Ah, looks interesting!

Just this error remaining:

error[E0615]: attempted to take value of method `hello_world` on type `&server::Server`
  --> src\server.rs:83:23
   |
83 |         let m = (self.hello_world)(self);
   |                       ^^^^^^^^^^^ help: use parentheses to call the method: `hello_world(...)`

To get the function as a value you want to say Self::hello_world (you get a fn item which can be cast to a fn pointer if needed). I don't have the definition of hello_world, but it will be a function and if it has a self argument, it expects that as the first argument.

For example:

struct Foo;

impl Foo {
    fn hello_world(&self) { println!("hello world!") }
}

fn main() {
    let func = Foo::hello_world;
    func(&Foo);
}
2 Likes

Ah

Nothing works, I tried a lot of things, and this does not works:

fn router_api(&self) -> nickel::Router {
    let mut router = Nickel::router();

    let m = (self.hello_world)(self);

    router.add_route(Method::Head, "/api/kv/:key", m);
}

Can you make a freestanding example, I'm sure we can fix it in that case?

1 Like

router.add_route expects a function, but (Self::hello_world)(self) is a value - the result of function evaluation. If you want to add a route with handler which always evaluates to the single value, you can try to make it explicitly through the closure:

fn router_api(&self) -> nickel::Router {
    let mut router = Nickel::router();

    let m = self.hello_world();

    router.add_route(Method::Head, "/api/kv/:key", || m);
    // or with some dummy arguments, like |_| m, - I can't find the signature for add_route
}
1 Like

Hey guys, this is the complete code, like you can see, the method hello_world need parameters:

fn hello_world<'mw>(&self, _req: &mut Request, res: Response<'mw>) -> MiddlewareResult<'mw> {
    match _req.param("key") {
        Some(key) => {
//                self.store.set(String::from("test"), String::from("test"));
        },
        _ => {
        }  // TODO: return error, missing key parameter
    }
    res.send("Hello World")
}

fn router_api(&self) -> nickel::Router {
    let mut router = Nickel::router();
    
    let m = self.hello_world();

    router.add_route(Method::Head, "/api/kv/:key", || m);
}

You can see the complete code in the repo here: https://github.com/clintnetwork/lucid/blob/development/src/server.rs#L85

Ok, so the thing you need is some kind of partial application - supply the first argument (self) and let Nickel add the remaining ones. Looks like this should work:

fn router_api(&self) -> nickel::Router {
    let mut router = Nickel::router();
    
    let m = |req, res| self.hello_world(req, res);

    router.add_route(Method::Head, "/api/kv/:key", m);
}

Or, if you prefer the more explicit way:

let m = |req, res| Self::hello_world(self, req, res);
1 Like

Ah yes, I see, I just have a lifetime issue now!

Maybe I need to learn rust before coding something :smiley: :smiley: :smiley:

1 Like

Learn by doing, you're going down the right path. You'll probably just go-back later and re-write some of the code as you get better

1 Like

Yep, In general making a project as first project in a particular language is not a good idea, but it also a good exercise!

By the way if anyone has a solution, it could be great :sweat_smile:

Okey, lets go with an other point of view ^^

extern crate nickel;

use nickel::{Nickel, HttpRouter, Request, Response, MiddlewareResult};

struct Server;

impl Server {
    fn hello_world<'mw>(&self, _req: &mut Request, res: Response<'mw>) -> MiddlewareResult<'mw> {
        res.send("Hello World")
    }

    fn default()
    {
        let mut server = Nickel::new();
        server.get("**", hello_world); // << how to use hello_world in the current scope
        server.listen("127.0.0.1:6767").unwrap();
    }
}

fn main() {
    Server::default();
}

Solution founded on the discord #beginner :smiley:

use nickel::{Nickel, HttpRouter, Middleware, Request, Response, MiddlewareResult};

struct Responder {}
impl<D> Middleware<D> for Responder {
    fn invoke<'mw, 'conn>(&self, _req: &mut Request<'mw, 'conn, D>, res: Response<'mw, D>) -> MiddlewareResult<'mw, D> {
        res.send("Hello World")
    }
}

struct Server;
impl Default for Server {
    fn default() -> Self {
        let mut server = Nickel::new();
        server.get("**", Responder {});
        server.listen("127.0.0.1:6767").unwrap();
        Server {}
    }
}

fn main() {
    Server::default();
}
1 Like