Reputation: 648
I have written the following code:
use std::str::{from_utf8};
struct JSONPointer {
segments: Vec<String>
}
fn build_json_pointer(s: Vec<String>) -> JSONPointer {
JSONPointer { segments: s.iter().map(|x| x.replace("~1", "/").replace("~0", "~")).collect() }
}
fn main() {
let v = vec!["foo".to_string(), "bar".to_string(), "baz~1".to_string()];
let p = build_json_pointer(v);
println!("Hello world! {:?}", p.segments);
}
This all works well with String
s, but ideally my JSONPointer
struct would contain a Vec<&str>
(if only for learning purposes). The issue I run into takes place in the call to map
the replaces a few reserved strings. No matter the combination of conversions I use, I am always told that my borrowed value does not live long enough
.
I understand that I am taking a reference to a local value and trying to return it (leading to a possible use-after-delete error), but I can't seem to find a way to take a copy of the string.
EDIT: Updated to minimum reproducible form
I realize now that when I was referring to wanting a "copy" (heap allocated), that's exactly what String
is. There was talk about a cleaner way of doing this possibly though.
Upvotes: 0
Views: 1990
Reputation: 430634
If you are worried about needlessly allocating strings, there is the possibility of using a Cow
:
use std::borrow::Cow;
struct JSONPointer<'a> {
segments: Vec<Cow<'a, str>>,
}
fn replace_if<'a>(s: Cow<'a, str>, from: &str, to: &str) -> Cow<'a, str> {
if s.contains(from) {
Cow::Owned(s.replace(from, to))
} else {
s
}
}
fn build_json_pointer<'a>(s: &[&'a str]) -> JSONPointer<'a> {
let segments = s
.iter()
.copied()
.map(Cow::Borrowed)
.map(|x| replace_if(x, "~1", "/"))
.map(|x| replace_if(x, "~0", "~"))
.collect();
JSONPointer { segments }
}
fn main() {
let v = vec!["foo", "bar", "baz~1"];
let p = build_json_pointer(&v);
println!("Hello world! {:?}", p.segments);
}
This has the advantage that it doesn't need to allocate any memory when no replacements need to be made, but has the downside that each pattern has be be searched twice. It could potentially be even more efficient with a structure like a rope.
For your original case that accepted a String
, you could do something similar without Cow
by keeping the original String
and not unconditionally replacing it:
struct JSONPointer {
segments: Vec<String>,
}
fn replace_if(s: String, from: &str, to: &str) -> String {
if s.contains(from) {
s.replace(from, to)
} else {
s
}
}
fn build_json_pointer(s: &[String]) -> JSONPointer {
let segments = s
.iter()
.cloned()
.map(|x| replace_if(x, "~1", "/"))
.map(|x| replace_if(x, "~0", "~"))
.collect();
JSONPointer { segments }
}
fn main() {
let v = vec!["foo".to_string(), "bar".to_string(), "baz~1".to_string()];
let p = build_json_pointer(&v);
println!("Hello world! {:?}", p.segments);
}
Upvotes: 4