E0369连接字符串引用时

时间:2015-12-19 11:39:48

标签: operator-overloading rust stringstream

下面的示例显示了尝试连接两个字符串的两个函数foo()bar()。如果左侧+参数为&String,则代码将无法编译。

这在foo()中举例说明,无法使用错误消息 [E0369] 进行编译。函数bar()表明我可以通过克隆LHS参数来摆脱这种情况。我的直觉表明这种克隆应该是多余的。

为什么我必须clone() LHS论证?这是Rust + - 运算符的当前实现状态的反映,还是有一个有意深思的背后?

// rustc 1.7.0-nightly (110df043b 2015-12-13)

fn foo(a: &String, i: i32) -> String {
    a + &i.to_string() // .. '+' cannot be applied to type & .. String [E0369]
}

fn bar(a: &String, i: i32) -> String { 
    a.clone() + &i.to_string() // Ok
}

#[test]
fn my_test() {
    assert!(foo(&"s".to_string(), 42) == "s42"); // fn foo(..) failed to compile
    assert!(bar(&"s".to_string(), 42) == "s42"); // Ok
}

2 个答案:

答案 0 :(得分:5)

如果您查看Add特征(或当前nightly docs for Add)的文档,您会看到:

impl<'a> Add<&'a str> for String

这是Add的{​​{1}}的唯一实现。此签名表示左侧按值而不是引用,因此需要调用String

要澄清一下,clone() Add<&str>只是String的封套,它附加到String::push_str的末尾,重新使用现有的分配。通过不可变指针执行此操作不可能,并且通过可变指针执行此操作将真的奇怪并且与合理的期望相反。因此,它要求LHS按值传递。

根据您的要求,为{em>任何形式的String定义的+运算符是疣,因此是否定义了其他表单是...好吧,就我所知,一个悬而未决的问题。

答案 1 :(得分:3)

&String是一件非常奇怪的事情。你几乎总是应该选择&str

引起我注意的是你说的

  

这意味着字符串a的外部克隆是必要的,并且意味着不会浪费周期。

好吧,你在浪费周期 - &i.to_string()分配然后丢弃它的缓冲区。

to_string是特质ToString的一部分。它默认为Display类型实现为

impl<T: fmt::Display + ?Sized> ToString for T {
    #[inline]
    fn to_string(&self) -> String {
        use core::fmt::Write;
        let mut buf = String::new();
        let _ = buf.write_fmt(format_args!("{}", self));
        buf.shrink_to_fit();
        buf
    }
}

请注意,这是直接写入String。你可以对foo喜欢

做同样的事情
use std::fmt::Write;

fn foo(a: &str, i: i32) -> String {
    let mut buf: String = a.into();
    write!(&mut buf, "{}", i).unwrap();
    buf
}

更好的方法是智能地将buf预先分配到足够的大小。这是最差a.len() + 11,因为i将为字符串贡献最多11个字符。如果您知道i处于更有限的范围内,您可能会做得更好。

use std::fmt::Write;

fn foo(a: &str, i: i32) -> String {
    let mut buf = String::with_capacity(a.len() + 11);
    write!(&mut buf, "{}{}", a, i).unwrap();
    buf
}

你可能在这一点上想知道为什么我这样做。这是一个合理的问题。

我不知道。