来自字符串引用参数的 Rust 可变字符串声明

Posted

技术标签:

【中文标题】来自字符串引用参数的 Rust 可变字符串声明【英文标题】:Rust mutable String declaration from String reference argument 【发布时间】:2021-02-23 13:11:59 【问题描述】:

我有

fn main() 
    let x = String::from("12");
    fun1(&x);

fn fun1(in_fun: &String) 
    let mut y = _______;
    y.push_str("z");
    println!("in fun ", y);

其中_____ 是基于参数in_fun 声明y 的代码。

起初我尝试了let mut y = *in_fun; 错误move occurs because '*in_fun' has type 'String', which does not implement the 'Copy' traitlet mut y = String::from(*in_fun); 也给出了同样的错误。

有效的是let mut y = String::from(format!("", *in_fun));

    这是从&String 声明可变String 的正确方法吗? 另外我还是不明白为什么用* 错误解引用&String?我理解 *& 取消引用只返回引用的值。

【问题讨论】:

你必须clone这个字符串。请参阅The Rust Programming Language: Understanding Ownership 了解更多信息。 let mut y = in_fun.clone() 有效,但 let mut y = *in_fun.clone() 无效。那不是我想克隆String 而不是&String (在函数中传递的)。 in_fun.clone() 将克隆String.clone() 方法通过引用获取 self,因此 String 实现比克隆引用本身匹配得更好。 @kmdreko 但是in_fun 不是作为&String 而不是String 传递到函数中吗?被引用事物的方法是否也传递给引用(例如.clone())?还是您的意思是修改签名以改为使用String “被引用事物的方法也传递给引用” - 通常,但它确实取决于函数签名(无论是 self 还是 &self ) 以及可用的其他选项。考虑查看this Q&A,它讨论了如何推导函数查找。 【参考方案1】:

&String 是一个不可变的引用。 Rust 对此非常严格,可以防止人们经常遇到的许多常见事故。取消引用 &String 是不可能的,因为它会破坏 rust 的安全保证,允许您修改只有读取权限的位置。请参阅ownership explanation。

该函数应该接受一个可变引用&mut String(然后可以就地修改字符串)或者它需要.clone()来自不可变引用的字符串。

采用可变引用比克隆更有效,但它限制了调用者以不可变的方式并行共享它。

如果您只想打印一些附加信息,我知道的最好方法是:

fn fun1<S: std::fmt::Display>(in_fun: S) 
    println!("in fun z", in_fun);


fn main() 
    let mut x = String::from("12");
    fun1(&x);
    fun1(&mut x);
    fun1(x);
    fun1("12");

我使用 Display 特征,所以任何实现都可以。见playground。

另一方面,如果你真的需要一个拥有的字符串,那么就要求吧:)

fn fun1<S: Into<String>>(in_fun: S) 
    let mut y = in_fun.into();
    y.push('z');
    println!("in fun ", y);


fn main() 
    let x = String::from("12");
    fun1(&x);
    fun1(x);
    fun1("12");

这样您可以同时接受 &str 和 String 并保持高效,尽可能避免克隆。

【讨论】:

这个let mut y = in_fun.into(); 会隐式克隆吗? @C14L,不是String,对于相同的类型,它是一个标识函数doc.rust-lang.org/src/core/convert/mod.rs.html#552-556,否则它取决于Into/From的实现。 &amp;str 必须被克隆。 有趣。但是,改变y 不会改变in_fun 也指向的相同数据吗? 啊,我的印象是引用&amp;和取消引用*之间的关系就像函数和反函数,s.t. f^-1(f(x)) = *&x @C14L,如果它是一个拥有的值,就像String,那么它会是可以的,因为除了当前范围之外没有其他人可以访问,String 无法访问任何其他方式(安全)。如果参数是&amp;str.into() 将克隆它,这样就可以了。【参考方案2】:

首先是工作代码:

fn fun1(in_fun: &String) 
    let mut y = in_fun.clone();
    y.push_str("z");
    println!("in fun ", y);

或者,您的直觉告诉您必须取消引用,因此 (*in_fun).clone() 的工作原理相同,但有点多余。 *in_fun.clone() NOT 有效,因为它等同于 *(in_fun.clone())(取消引用克隆),这不是您想要的。在调用clone 之前不需要取消引用引用的原因是因为Rust's method resolution 允许您调用类型的方法或使用对该类型的引用访问类型的属性,而.clone 有一个@987654328 @接收者。

let mut y = *in_fun 不起作用的原因是因为它试图将字符串从引用下方移出,这不起作用。

【讨论】:

我没有意识到.clone()出现在*之前,我以为优先级只是从左到右。

以上是关于来自字符串引用参数的 Rust 可变字符串声明的主要内容,如果未能解决你的问题,请参考以下文章

Rust 编译器不期望一个可变引用,而应该期望一个可变引用

Rust 中的可变引用传递

rust - 为什么对已删除对象的可变引用仍算作可变引用?

python中变量的引用、可变和不可变类型、局部变量和全局变量

为啥在 Rust 中将 const 引用直接转换为可变引用无效?

如何在 Rust 中传递对可变数据的引用?