String new. In Rust we often need to create new strings. We can use the new() or from() functions for this—or the to_owned() function can be used on a literal.
String references. To receive a str reference, we can borrow a String with the ampersand operator. This gives us string data behind a reference that we cannot change.
New example. For creating new Strings in Rust, it is best to start with a String function like new() or from(). This often leads to the simplest code.
Version 1 We use new() and then call push_str to append some data to the string that was just allocated.
Version 2 We use the String from() function to initialize a string from a str reference. The "cat" literal is a str reference.
Version 3 We use to_owned() on a literal to create a String from it. This syntax is probably the least clear.
fn main() {
// Version 1: use new.
let mut first = String::new();
first.push_str("cat");
println!("{first}");
// Version 2: use from.
let second = String::from("cat");
println!("{second}");
// Version 3: use to_owned.
let third = "cat".to_owned();
println!("{third}");
}cat
cat
cat
Borrow string. When we borrow a String, we get a str reference. Here the print_len() function receives a str reference, and we pass a String to it with borrowing.
fn print_len(value: &str) {
println!("LEN = {}", value.len());
}
fn main() {
let mut value = String::new();
value.push_str("abc");
// We can borrow the string to use it as a str reference.
print_len(&value);
}LEN = 3
String cache performance. Suppose we want to allocate a short-lived temporary string and use it for some reason. This could happen many times in a program.
Version 1 We use String new() to allocate a new string on each iteration of the benchmarked loop.
Version 2 We access the RefCell cache on the Example struct, calling borrow_mut(), and then reuse the existing String.
Result It is nearly 10 times faster to reuse an already-allocated String, even if we have to access it through a RefCell.
Info When we call String new(), we must use the memory allocator. And this has significant overhead, even though Rust is not garbage-collected.
use std::cell::*;
use std::time::*;
struct Example {
cache: RefCell<String>,
}
fn main() {
let ex = Example {
cache: RefCell::new(String::new()),
};
if let Ok(max) = "100000000".parse::<usize>() {
// Version 1: use String new.
let t0 = Instant::now();
for _ in 0..max {
let mut temp = String::new();
temp.push('z');
temp.push_str("hello");
}
println!("{} ms", t0.elapsed().as_millis());
// Version 2: use RefCell cache String.
let t1 = Instant::now();
for _ in 0..max {
let mut temp = ex.cache.borrow_mut();
temp.clear();
temp.push('z');
temp.push_str("hello");
}
println!("{} ms", t1.elapsed().as_millis());
}
}1299 ms String new
145 ms Borrow_mut, String clear
Summary. We reviewed the String new(), from, and to_owned functions. We learned how to get a str from a String instance. And we benchmarked a String cache, which could help performance.
Dot Net Perls is a collection of tested code examples. Pages are continually updated to stay current, with code correctness a top priority.
Sam Allen is passionate about computer languages. In the past, his work has been recommended by Apple and Microsoft and he has studied computers at a selective university in the United States.
This page was last updated on Jul 15, 2023 (edit).