本文介绍了在HashMap中存储带有引用arg的未装箱的闭包的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我试图将闭包存储为HashMap值。如果我通过值传递闭包arg,一切都很好:

 使用std :: collections :: hash_map :: HashMap; 

fn main(){
let mut cmds:HashMap< String,Box< FnMut(String) - >()>
= HashMap :: new();

cmds.insert(ping.to_string(),Box :: new(|& mut:s | {println!({},s);}));

match cmds.get_mut(ping){
Some(f)=> f(pong.to_string()),
_ => ()
}
}

()



但是如果我想要一个包含引用arg的闭包,

 使用std :: collections :: hash_map :: HashMap; 

fn main(){
let mut cmds:HashMap< String,Box< FnMut(& str) - >()>
= HashMap :: new();

cmds.insert(ping.to_string(),Box :: new(|& mut:s | {println!({},s);}));

match cmds.get_mut(ping){
Some(f)=> f(pong),
_ => ()
}
}


< anon>:8:37:8:78错误:类型不匹配:类型`closure [< anon> :8:46:8:77]`实现trait`core :: ops :: FnMut(_)`,但是`for` core:ops :: FnMut(&'r str)`是必需的(预期的具体生存期,找到的绑定生命周期参数)
< anon>:8 cmds.insert(ping.to_string :: new(|& mut:s | {println!({},s);}));
^ ~~~~~~~~~~~~~~~~~~~~~~
< ; anon>:8:37:8:78注意:需要转换为对象类型`for<'r> core :: ops :: FnMut(&'r str)`
< anon>:8 cmds.insert(ping.to_string(),Box :: new(|& mut:s | println!({},s);}));
^ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
错误:由于先前的错误而中止

()



我阅读了,并尝试打破地图构建到自己的功能,以有一个地方挂起其中子句,但没有骰子:

  use std :: collections: :hash_map :: HashMap; 

fn mk_map< F>() - > HashMap< String,(String,Box< F>)>
其中F:for<'a> FnMut(& a str) - > ()
{
let mut cmds:HashMap< String,(String,Box< F>)> = HashMap :: new();
cmds.insert(ping.to_string(),(ping.to_string(),Box :: new(|& mut:s | {println!({},s);} )));
cmds
}

fn main(){
let cmds = mk_map();
match cmds.get_mut(ping){
一些(& mut(_,ref mut f))=> f(pong),
_ => println!(invalid command)
}
}


< anon>:8:58:8:99错误: ; F>`,found`Box< closure [< anon>:8:67:8:98]>`(expected type parameter,found closure)
< anon>:8 cmds.insert ping.to_string(),(ping.to_string(),Box :: new(|& mut:s | {println!({},s);})));
^ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

幼儿围栏

解决方案



我的解决方案:

 #![allow(unstable)] 
use std :: collections :: hash_map :: HashMap ;

//#1返回一个trait对象
fn mk_map<'a>() - > HashMap< String,(String,Box< FnMut(& str)+'a>)> {
let mut cmds:HashMap< _,(_,Box< FnMut(& str)>)& = HashMap :: new();

cmds.insert(ping.to_string(),(ping.to_string(),
Box :: new(|& mut:s:& str | println!({},s);})));
//#2 ^ - 给编译器一点帮助
cmds
}

fn main(){
let mut cmds = mk_map();
//小改变:cmds需要是可变的
match cmds.get_mut(ping){
一些(& mut(_,ref mut f))=> f(pong),
_ => println!(invalid command)
}
}

/ p>


  1. 返回一个trait对象

  2. 根据closure参数的类型为编译器提供一些帮助: Box :: new(& mut:s:& str |

老实说,我不是100%确定#2的原因(我的意思是,至少应该给出一个更清楚的错误消息)。可能是rustc的问题。



在#1,我几乎肯定它是必需的,因为你不能为从函数返回的闭包命名一个具体的返回类型(它是由编译器在运行时创建的匿名类型)因此现在的Trait对象应该是返回闭包的唯一方法。



附录
响应注释:

假设您有一个 trait Foo {}

  trait Foo {} 
impl Foo for u32 {}
impl Foo for Vec< f32& {}

如果你编写一个类似于mk_map的函数(我们称之为make_foo)它将很难实现它。让我们看看:

  fn mk_foo< F>() - > Box< F&其中F:Foo {
未实现!()
}

mk_foo说,我应该能够调用该函数与任何类型实现Foo。所以这应该都是有效的:

  let a:Box< Vec< f32> = mk_foo ::< Vec< f32>>(); 
let b:Box< u32> = mk_foo ::< u32>();

函数,如写,不返回trait对象。它有希望返回一个框与任何具体类型的调用者选择。这就是为什么它不容易实际实现的功能。它应该知道如何从一无所有创建几种类型。


I'm trying to store a closure as a HashMap value. If I pass the closure arg by value, everything works great:

use std::collections::hash_map::HashMap;

fn main() {
    let mut cmds: HashMap<String, Box<FnMut(String)->()>>
        = HashMap::new();

    cmds.insert("ping".to_string(), Box::new(|&mut:s| { println!("{}", s); }));

    match cmds.get_mut("ping") {
        Some(f) => f("pong".to_string()),
        _ => ()
    }
}

(playpen)

But if I want a closure that takes a reference arg, things go south:

use std::collections::hash_map::HashMap;

fn main() {
    let mut cmds: HashMap<String, Box<FnMut(&str)->()>>
        = HashMap::new();

    cmds.insert("ping".to_string(), Box::new(|&mut:s| { println!("{}", s); }));

    match cmds.get_mut("ping") {
        Some(f) => f("pong"),
        _ => ()
    }
}


<anon>:8:37: 8:78 error: type mismatch: the type `closure[<anon>:8:46: 8:77]` implements the trait `core::ops::FnMut(_)`, but the trait `for<'r> core::ops::FnMut(&'r str)` is required (expected concrete lifetime, found bound lifetime parameter )
<anon>:8     cmds.insert("ping".to_string(), Box::new(|&mut:s| { println!("{}", s); }));
                                             ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
<anon>:8:37: 8:78 note: required for the cast to the object type `for<'r> core::ops::FnMut(&'r str)`
<anon>:8     cmds.insert("ping".to_string(), Box::new(|&mut:s| { println!("{}", s); }));
                                             ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
error: aborting due to previous error

(playpen)

I read the answer to How to rewrite code to new unboxed closures, and tried breaking out the map building into its own function in order to have a place to hang the where clause, but no dice:

use std::collections::hash_map::HashMap;

fn mk_map<F>() -> HashMap<String, (String, Box<F>)>
    where F: for<'a> FnMut(&'a str) -> ()
{
    let mut cmds: HashMap<String, (String, Box<F>)> = HashMap::new();
    cmds.insert("ping".to_string(), ("ping".to_string(), Box::new(|&mut:s| { println!("{}", s); })));
    cmds
}   

fn main() {
    let cmds = mk_map();
    match cmds.get_mut("ping") {
        Some(&mut (_, ref mut f)) => f("pong"),
        _ => println!("invalid command")
    }
}


<anon>:8:58: 8:99 error: mismatched types: expected `Box<F>`, found `Box<closure[<anon>:8:67: 8:98]>` (expected type parameter, found closure)
<anon>:8     cmds.insert("ping".to_string(), ("ping".to_string(), Box::new(|&mut:s| { println!("{}", s); })));
                                                                  ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

(playpen)

What's the right way to do this?

解决方案

My solution:

#![allow(unstable)]
use std::collections::hash_map::HashMap;

// #1 returning a trait object   
fn mk_map<'a>() -> HashMap<String, (String, Box<FnMut(&str) + 'a>)> {
    let mut cmds : HashMap<_, (_, Box<FnMut(&str)>)> = HashMap::new();

    cmds.insert("ping".to_string(), ("ping".to_string(), 
        Box::new(|&mut: s: &str| { println!("{}", s); })));
    // #2                  ^-- give a little help to the compiler here
    cmds
}   

fn main() {
    let mut cmds = mk_map();
    // minor change: cmds needs to be mutable
    match cmds.get_mut("ping") {
        Some(&mut (_, ref mut f)) => f("pong"),
        _ => println!("invalid command")
    }
}

Ingredients:

  1. return a trait object
  2. give some help to the compiler on the type of the closure's parameter: Box::new(|&mut: s: &str|

To be honest, I'm not 100% sure about the reason for #2 (I mean, at least leaving it out should give a more intelligible error message). Probably an issue with rustc.

On #1, I'm almost sure it's required because you can't name a concrete return type for a closure returned from a function (it's an anonymous type created on the fly by the compiler), so Trait objects for now should be the only way to return a closure.

Appendixresponding to comment:

imagine you have a trait Foo {} implemented by a few types:

trait Foo {}
impl Foo for u32 {}
impl Foo for Vec<f32> {}

if you write a function like you did with mk_map (let's call it make_foo) I commented that it's going to be hard to implement it. Let's see:

fn mk_foo<F>() -> Box<F> where F: Foo {
    unimplemented!()
}

the signature of mk_foo says that I should be able to call the function with any type that implements Foo. So this should all be valid:

   let a: Box<Vec<f32>> = mk_foo::<Vec<f32>>();
   let b: Box<u32> = mk_foo::<u32>();

i.e. the function, as written, is not returning a trait object. It's promising to return a Box with any concrete type the caller chooses. That's why it's not easy to actually implement the function. It should know how to create several types from nothing.

这篇关于在HashMap中存储带有引用arg的未装箱的闭包的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!

10-27 00:20