传递函数和方法作为参数有什么区别?

时间:2015-01-17 19:30:20

标签: rust

就像我的a previous question一样,我正在重构一个小的Rust项目。与该问题类似,编译器和我也没有在方法和价值观上保持一致。但是,错误消息非常不同,并且此似乎可能涉及库,hyper在此示例中。

/* extern and use directives... */

pub struct WebServer /* with stuff not relevant to the error */;

impl WebServer {
    pub fn serve(&mut self) {
        let server = Server::http(Ipv4Addr(127, 0, 0, 1), 80);
        let mut listening = server.listen(self.return_page).unwrap();
        listening.await();
    }

    fn return_page(&mut self, req: Request, mut res: Response) {
        /* Put the page together */
    }
}

serve()return_page()只是main.rs中的函数时,这会编译并运行。

如上所述隐藏在struct中,我收到以下错误:

.../src/main.rs:13:48: 13:59 error: attempted to take value of method `return_page` on type `&mut WebServer`
.../src/main.rs:13         let mut listening = server.listen(self.return_page).unwrap();
                                                                  ^~~~~~~~~~~
.../src/main.rs:13:48: 13:59 help: maybe a `()` to call it is missing? If not, try an anonymous function
.../src/main.rs:13         let mut listening = server.listen(self.return_page).unwrap();
                                                                  ^~~~~~~~~~~

我认为解析器只需要朝着正确的方向轻推,但即使我对相关问题给出了建议,我也没有在文档中看到任何可以说明的方式,&#34 ;不,我的意思是将函数作为参数,就像我需要的调用函数的签名一样。"鉴于上述语法在struct

之外工作,我错过了什么

对于关闭的半心半意的尝试似乎更糟错误消息仍然建议调用return_page而不是传递它,但我很可能误解了这种情况下的用法

修改:为了澄清使用现有答案后的情况,原来的return_page()当然没有self参数,这本身就是部分问题。但它在对象内部也需要self,因为它需要访问成员变量。

2 个答案:

答案 0 :(得分:3)

您无法将return_page直接传递给listen,因为Handler仅适用于带有2个参数,请求和响应的函数。 return_page需要3个参数(self计数作为参数)。

试试这个:

let mut listening = server.listen(|&: request, response| self.return_page(request, response)).unwrap();

或者,为impl添加Handler,然后将return_page功能移至名为impl的{​​{1}}。

handle

请注意impl WebServer { pub fn serve(&mut self) { let server = Server::http(Ipv4Addr(127, 0, 0, 1), 80); let mut listening = server.listen(&self).unwrap(); listening.await(); } } impl<'a> Handler for &'a WebServer { fn handle(&self, req: Request, mut res: Response) { /* Put the page together */ } } 通过不可变引用传递Handler,而不是通过可变引用传递。您可能需要使用RefCell来绕过这一点。

答案 1 :(得分:1)

简短的回答是你可以使用UFCS(统一函数调用语法):

let mut listening = server.listen(WebServer::return_page).unwrap();

答案很长,我怀疑这会使你的代码工作。您没有显示直接在main中定义return_page的方式,但我想它类似于fn return_page(req: Request, mut res: Response)

此签名与定义为WebServer方法的return_page不同。 我们来看看它:

fn return_page(&mut self, req: Request, mut res: Response)

这是一个采用&mut self类型的第一个参数的函数,即&mut WebServer。如果listen没有预料到这个签名,您将会得到一个&#34;不匹配的类型&#34;错误。

让我尝试创建代码的模拟实现:

// serve calls listen passing it return_page. Note that
// the signature of return_page matches the one that
// listen is expecting, fn(u32, u32)
fn serve() {
    listen(return_page)
}

// mock return_page
fn return_page(a: u32, b: u32) { println!("{}, {}", a, b) }

// listen expects a function that takes two u32
// and returns nothing
fn listen( f: fn(u32, u32) ) { f(1, 2) }

fn main() {
    serve();
}

playpen

现在让我们将serve和return_page移动到impl中,就像我认为你做的那样:

fn listen( f: fn(u32, u32) ) { f(1, 2) }

struct WebServer;
impl WebServer {
    fn serve(&self) {
        listen(WebServer::return_page)
    }

    // a method is a function that takes the implementing type
    // as a first parameter. We can use UFCS to invoke it as 
    // a function: WebServer::return_page
    fn return_page(&self, a: u32, b: u32) { println!("{}, {}", a, b) }
}

playpen

糟糕,不匹配的类型:listen不期望第一个类型为&WebServer的参数。你需要这个签名: fn listen(f: fn(&WebServer, u32, u32) )

请注意,如果假设listen确实有该签名,我们的程序就可以运行。请参阅最后一个playpen示例