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 pages with code examples, which are updated to stay current. Programming is an art, and it can be learned from examples.
Donate to this site to help offset the costs of running the server. Sites like this will cease to exist if there is no financial support for them.
Sam Allen is passionate about computer languages, and he maintains 100% of the material available on this website. He hopes it makes the world a nicer place.
This page was last updated on Jul 15, 2023 (edit).