Reputation: 2151
I am writing a method to loop through a (from, to)
of a map and perform multiple rounds of tmp = tmp.replace(from, to)
. I am still trying to grasp the ownership concepts of 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() {}
This code gets me...
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`
The extra a
and b
are my attempts to get by why Rust made from
and to
become &&str
.
Upvotes: 2
Views: 1749
Reputation: 431789
The first problem is your return value: &str
. You are returning a reference to something, but what will own that value? You cannot return a reference to a local variable.
The second problem is the return type of str::replace
, which is a String
, not a &str
. That's the cause of your error message: you are attempting to store a String
in a variable where only a &str
can be stored. You cannot do that.
The easiest fix is not the most efficient; unconditionally create a 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
}
You could also use a type like Cow
to save a little bit of allocation in some cases:
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()
}
Which can even be returned so that no allocation occurs if no replacements exist:
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
}
Or the functional equivalent using 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())
}
It's unfortunate that str::replace
doesn't return a Cow<str>
. If it did, no allocations would take place if no replacements were made.
See also:
Upvotes: 5