避免在匹配语句中使用部分移动的值

Avoid using partially moved value in match statement

问题中提供的解决方案对我不起作用。

考虑这些类型和函数:

pub enum Parameter {
    ParameterTypeA { n: i32 },
    ParameterTypeB { s: String },
}

pub struct Res {
    pub param: Parameter,
    pub res: i32,
}

n use_string(_: String) -> i32 {
    return 23;
}

fn use_int(_: i32) -> i32 {
    return 42;
}

我想达到的效果是这样的:

fn foobar(p: Parameter) -> std::result::Result<Res, String> {
    return match p {
        Parameter::ParameterTypeA { n } => { Ok(Result { param: p, res: use_int(n) }) }
        Parameter::ParameterTypeB { s } => { Ok(Result { param: p, res: use_string(s) }) }
    };
}

但是编译器抱怨 Parameter::ParameterTypeB { s } 被部分移动并拒绝将其与 param: p 一起使用。

根据编译器的建议和提到的SO线程,我将第二个匹配更改为

Parameter::ParameterTypeB { ref s } => { Ok(Result { param: p, res: use_string(s.to_string()) }) }

但编译器再次抱怨 {ref s} 借用了 p.s,在创建结果时无法移动。

这个问题应该怎么解决?请理解,修改函数签名或数据类型不是一种选择。

MVE 在 play.rust-lang.org 可用。

解决此问题的最惯用方法是更改​​ use_string 的签名,使其采用 &str 而不是 String:

pub enum Parameter {
    ParameterTypeA { n: i32 },
    ParameterTypeB { s: String },
}

pub struct Res {
    pub param: Parameter,
    pub res: i32,
}

fn use_string(_: &str) -> i32 {
    return 23;
}

fn use_int(_: i32) -> i32 {
    return 42;
}

fn foobar(p: Parameter) -> std::result::Result<Res, String> {
    return match p {
        Parameter::ParameterTypeA { n } => { Ok(Res { param: p, res: use_int(n) }) }
        Parameter::ParameterTypeB { ref s } => { Ok(Res { res: use_string (s), param: p }) }
    };
}

Playground

请注意,我已经更改了 resparam 的顺序,以便在实例化 Resres 排在第一位。这是因为它们是从左到右求值的,首先使用 param: p 会移动 p 并且无法在后续调用中使用 s(借用 p)至 use_string.

由于您声明更改签名不是一个选项,因此您需要克隆字符串,以便 p 在调用 use_string 后保留一个副本:

pub enum Parameter {
    ParameterTypeA { n: i32 },
    ParameterTypeB { s: String },
}

pub struct Res {
    pub param: Parameter,
    pub res: i32,
}

fn use_string(_: String) -> i32 {
    return 23;
}

fn use_int(_: i32) -> i32 {
    return 42;
}

fn foobar(p: Parameter) -> std::result::Result<Res, String> {
    return match p {
        Parameter::ParameterTypeA { n } => { Ok(Res { param: p, res: use_int(n) }) }
        Parameter::ParameterTypeB { ref s } => { Ok(Res { res: use_string (s.clone()), param: p }) }
    };
}

Playground