fn main() {
// &'static str
let this = "Hello";
// String
let that: String = String::from("Hello");
// &str
let other = that.as_str();
}There are several different kinds of strings in Rust.
Most common are String and &str.
StringOwns the data it stores, and can be mutated freely.
Exists as a pointer to some bytes, a length, and a capacity.
Exists on the heap.
Does not implement Copy, but implements Clone.
&strAn immutable reference to a string slice.
Only seen as a borrowed value.
May be anywhere, on the heap, stack, or in program memory.
fn main() {
// &'static str
let this = "Hello";
// String
let that: String = String::from("Hello");
// &str
let other = that.as_str();
}String is the easiest to use when starting out. Refine later.
String owns its data, so works well as a field of a struct or Enum.
&str is typically used in function arguments.
Deref CoercionJust because multiple types exist doesn’t mean they can’t work in harmony.
fn main() {
let part_one = String::from("Hello ");
let part_two = String::from("there ");
let whole = part_one + &part_two + "world!";
println!("{}", whole);
}This is because String s implement Deref<Target=str> .
OsStr and OsString may show up when working with file systems or system calls.
CStr and CString may show up when working with FFI.
The differences between [Os|C]Str and [Os|C]String are generally the same as the normal types.
OsString & OsStrThese types represent platform native strings. This is necessary because Unix and Windows strings have different characteristics.
OsString ScenesUnix strings are often arbitrary non-zero sequences, usually interpreted as UTF-8.
Windows strings are often arbitrary non-zero sequences, usually interpreted as UTF-16.
Rust strings are always valid UTF-8, and may contain zeros.
OsString and OsStr bridge this gap and allow for cheap converstion to and from String and str.
CString & CStrThese types represent valid C compatible strings.
They are predominantly used when doing FFI with external code.
It is strongly recommended you read all of the documentation on these types before using them.
Splitting:
fn main() {
let words = "Cow says moo";
let each: Vec<_> = words.split(" ").collect();
println!("{:?}", each);
}Concatenation:
fn main() {
let animal = String::from("Cow");
let sound = String::from("moo");
let words = [&animal, " says ", &sound].concat();
println!("{:?}", words);
}Replacing:
fn main() {
let words = "Cow says moo";
let replaced = words.replace("moo", "roar");
println!("{}", replaced);
}String or strIt’s possible to accept either rather painlessly:
fn accept_either<S>(thing: S) -> String
where S: AsRef<str> {
String::from("foo") + thing.as_ref()
}
fn main() {
println!("{}", accept_either("blah"));
println!("{}", accept_either(String::from("blah")));
}Starts with r followed by zero or more # followed by "
Ends with " followed by the same number of #
Can span multiple lines, leading spaces become part of the line
Escape sequences are not processed
fn main () {
let json = r##"
{
"name": "Rust Analyzer",
"brandColor": "#5bbad5"
}
"##;
assert_eq!(r"\n", "\\n");
}not really strings
used to declare static byte slices (have a &[u8] type)
fn main() {
let byte_string: &[u8] = b"allows ASCII and \xF0\x9F\x98\x80 only";
println!("Can Debug fmt but not Display fmt: {:?}", byte_string);
if let Ok(string) = std::str::from_utf8(byte_string) {
println!("Now can Display '{}'", string);
}
}