【问题标题】:How to repeatedly perform string replacement in a loop?如何在循环中重复执行字符串替换?
【发布时间】:2018-09-10 04:10:05
【问题描述】:

我正在编写一个方法来循环遍历地图的(from, to) 并执行多轮tmp = tmp.replace(from, to)。我仍在尝试掌握 Rust 的所有权概念

#[macro_use]
extern crate lazy_static;

use std::collections::HashMap;

lazy_static! {
    static ref REPLACEMENTS: HashMap<&'static str, &'static str> = {
        let mut m = HashMap::new();
        m.insert("abc", "def");
        m.insert("com", "org");
        m
    };
}

fn replace_path_name(path: &str) -> &str {
    let mut tmp = path;

    for (from, to) in REPLACEMENTS.iter() {
        let a = *from;
        let b = *to;

        tmp = tmp.replace(a, b);
    }

    tmp
}

fn main() {}

这段代码让我...

error[E0308]: mismatched types
  --> src/main.rs:22:15
   |
22 |         tmp = tmp.replace(a, b);
   |               ^^^^^^^^^^^^^^^^^
   |               |
   |               expected &str, found struct `std::string::String`
   |               help: consider borrowing here: `&tmp.replace(a, b)`
   |
   = note: expected type `&str`
              found type `std::string::String`

额外的ab 是我试图弄明白为什么Rust 将fromto 变成&amp;&amp;str

【问题讨论】:

    标签: string replace rust ownership


    【解决方案1】:

    第一个问题是你的返回值:&amp;str。您正在返回对某物的引用,但 拥有该值是什么?您不能返回对局部变量的引用。

    第二个问题是str::replace的返回类型,它是String,而不是&amp;str。这就是您的错误消息的原因:您试图将String 存储在一个只能存储&amp;str 的变量中。你不能那样做。

    最简单的修复并不是最有效的;无条件创建String

    fn replace_path_name(path: &str) -> String {
        let mut tmp = String::from(path);
    
        for (from, to) in REPLACEMENTS.iter() {
            tmp = tmp.replace(from, to);
        }
    
        tmp
    }
    

    在某些情况下,您还可以使用像 Cow 这样的类型来节省一点分配:

    use std::borrow::Cow;
    
    fn replace_path_name(path: &str) -> String {
        let mut tmp = Cow::from(path);
    
        for (from, to) in &*REPLACEMENTS {
            tmp = tmp.replace(from, to).into();
        }
    
        tmp.into()
    }
    

    甚至可以返回,这样如果不存在替换就不会发生分配:

    use std::borrow::Cow;
    
    fn replace_path_name(path: &str) -> Cow<str> {
        let mut tmp = Cow::from(path);
    
        for (from, to) in &*REPLACEMENTS {
            tmp = tmp.replace(from, to).into();
        }
    
        tmp
    }
    

    或使用 Iterator::fold 的等效功能:

    use std::borrow::Cow;
    
    fn replace_path_name(path: &str) -> Cow<str> {
        REPLACEMENTS
            .iter()
            .fold(Cow::from(path), |s, (from, to)| s.replace(from, to).into())
    }
    

    很遗憾str::replace 没有返回Cow&lt;str&gt;。如果是这样,如果没有进行替换,则不会进行任何分配。

    另见:

    【讨论】:

    • 非常感谢。这是我第四次尝试从 Java 中学习 Rust。感谢您的耐心等待!
    • 这仍然是正确的方法吗? tmp 上的某种 .map() 会不会更有效率?
    • @Jay 你建议在哪里实现map 方法?
    • @Shepmaster 查了一下,好像我把它和reduce搞混了。在 Python 中,它类似于 - reduce(lambda orig_str, replace_pairs: orig_str.replace(*replace_pairs), [('word', new_word'), ('word2', 'new_word2')], 'word word2')
    • @Jay 当然,like this。我预计不会有任何重大的性能差异。
    猜你喜欢
    • 2017-03-12
    • 2019-03-17
    • 2020-10-08
    • 2017-08-15
    • 2017-04-21
    • 2017-12-19
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多