Rust 如何处理从HashMap获得的值

Rust 如何处理从HashMap获得的值,rust,Rust,我希望我的getv()函数从HashMap返回值。无论我如何修改代码,都会出现错误: enum Type { TyInt, TyBool, } struct TypeEnv { Env: HashMap<char, Type>, } impl TypeEnv { fn set(&mut self, name: char, ty: Type) { self.Env.insert(name, ty); } fn

我希望我的
getv()
函数从
HashMap
返回值。无论我如何修改代码,都会出现错误:

enum Type {
    TyInt,
    TyBool,
}

struct TypeEnv {
    Env: HashMap<char, Type>,
}
impl TypeEnv {
    fn set(&mut self, name: char, ty: Type) {
        self.Env.insert(name, ty);
    }

    fn getv(self, name: char) -> Type {
        match self.Env.get(&name) {
            Some(Type) => Type, // <------- Always error here
            None => Type::TyInt,
        }
    }
}
枚举类型{
TyInt,
提布尔,
}
结构类型env{
Env:HashMap,
}
impl类型env{
fn集合(&mut self,名称:char,ty:Type){
自我环境插入(名称,ty);
}
fn getv(self,name:char)->Type{
匹配self.Env.get(&name){
Some(Type)=>Type,//Type::TyInt,
}
}
}

HashMap::get
返回一个
选项,而不是
选项,这就是为什么仅返回匹配值无法编译的原因

get
提供了一个引用,因为在Rust中,您不能简单地返回哈希表中的实际值-您需要克隆它(制作一个副本,可能很昂贵),将其从容器中删除并传输给调用方,或者返回对其中值的引用
HashMap::get
选择最后一个选项,因为它价格便宜,同时为调用者提供了最大的灵活性,调用者可以选择检查值或克隆值

对于由单个机器字组成的枚举,复制值将是最佳方法。(对于更复杂的枚举,更好的方法是返回引用,如中所示。)复制需要:

  • 通过在类型定义处使用
    #[派生(复制、克隆)]
    指令标记特征,使
    类型可复制

  • 通过使用
    *matched_value
    取消对匹配值的引用,或在模式中使用
    &
    getv
    返回副本

最后,您的
getv
方法将使用该对象,这意味着您只能调用它一次。这几乎肯定不是我们想要的——它应该接受
&self
。通过这些更改,生成的代码如下所示:

use std::collections::HashMap;

#[derive(Copy, Clone)]
enum Type {
    TyInt,
    TyBool,
}

struct TypeEnv {
    env: HashMap<char, Type>,
}

impl TypeEnv {
    fn set(&mut self, name: char, ty: Type) {
        self.env.insert(name, ty);
    }

    fn getv(&self, name: char) -> Type {
        match self.env.get(&name) {
            Some(&v) => v,
            None => Type::TyInt,
        }
    }
}
fn getv(&self, name: char) -> &Type {
    self.env.get(&name).unwrap_or(&Type::TyInt)
}
使用std::collections::HashMap;
#[衍生(复制、克隆)]
枚举类型{
TyInt,
提布尔,
}
结构类型env{
env:HashMap,
}
impl类型env{
fn集合(&mut self,名称:char,ty:Type){
自我环境插入(名称,ty);
}
fn getv(&self,名称:char)->Type{
匹配self.env.get(&name){
一些(&v)=>v,
None=>Type::TyInt,
}
}
}
如果类型需要包含非
复制
数据,则您可以将其改为仅
克隆
,并返回
v.Clone()

您不必使类型
复制
克隆
。。。如果你对推荐人满意的话

当编写Rust时出现这种情况时,通常最好重新评估调用代码的方式。可以简化吗

您可以让调用者期望使用
选项
,也可以强制执行某些操作。。。也许是这样的:

use std::collections::HashMap;

#[derive(Copy, Clone)]
enum Type {
    TyInt,
    TyBool,
}

struct TypeEnv {
    env: HashMap<char, Type>,
}

impl TypeEnv {
    fn set(&mut self, name: char, ty: Type) {
        self.env.insert(name, ty);
    }

    fn getv(&self, name: char) -> Type {
        match self.env.get(&name) {
            Some(&v) => v,
            None => Type::TyInt,
        }
    }
}
fn getv(&self, name: char) -> &Type {
    self.env.get(&name).unwrap_or(&Type::TyInt)
}
这简化了您的代码,并清楚地表明了您的目标:

使用std::collections::HashMap;
#[导出(调试)]
枚举类型{
TyInt,
提布尔,
}
结构类型env{
env:HashMap,
}
impl类型env{
fn集合(&mut self,名称:char,ty:Type){
自我环境插入(名称,ty);
}
fn getv(&self,名称:char)->类型(&T){
self.env.get(&name).unwrap_或(&Type::TyInt)
}
}
fn main(){
设mut te=TypeEnv{
env:HashMap::new(),
};
{
设arg=te.getv('c');
println!(“{:?}”,arg);/“TyInt”-因为找不到任何内容
}
te.set('c',Type::TyBool);
让arg=te.getv('c');/“TyBool”-因为我们将其存储在上面的行中。
println!(“{:?}”,arg);
}

另外,您现有的
getv
实现拥有
self
。。。注意我是如何添加引用的
getv(&self)
。如果没有,则此类型(据我所知)将变为。

我得到“匹配臂具有不兼容类型”错误。如果我将此行修改为Some(Type)=>*类型,那么我会得到“无法移出借用的内容”。如何实现这一目标(在您的问题中显示完整的编译器错误。这很有帮助。
Type
是枚举名称(或类型),不是它的值,而
Type::TyInt
是枚举值。根据错误消息,这些类型不同。这可能类似于在一个臂中返回
f64
,在另一个臂中返回
1.0
。尝试与
Some(x)匹配=>x,
x
应该是
类型
,并且您希望返回它的值(就我理解您的代码而言)。很可能(同样,如果我正确理解您的代码),您可能应该使用:这将返回选项内的值,或默认值。虽然可能是OP的疏忽,但您尚未解决
TypeEnv
的所有权问题。
getv
函数拥有
self
…的所有权,因此…即使在您不使用它时它也会编译。@SimonWhitehead Good point、 我现在已经修复了
getv
以接受
&self
,这几乎可以肯定是OP想要的。Rust的类型系统非常好,几乎让我们对“如果它能编译,它就是完美的”感到厌烦心态。如果
类型
像C样式枚举一样简单,即如果它适合机器字,那么通过引用返回它是没有意义的,就像通过引用返回
usize
很少有意义一样。这样做是有效的,但不会带来性能好处(事实上可能会导致惩罚),同时对API的人机工程学产生了负面影响。你的观点是@user4815162342。我认为提供一个替代方案仍然有助于OP。我通常假设提供的示例的范围比实际问题小得多,因为我们确实希望有一个MCVE,有时人们会选择小型/快速复制类型。同意-如果
Type
很重,你应该避免复制它