Rust DISCUSSION

Rust String vs &str: which one should function parameters and struct fields use?

Started by abu Rust Stringstr sliceownershipborrowingUTF-8 strings
5 replies 248 views 6 participants
Latest activity · 30 Sep 2026

Rust String vs &str: which one should function parameters and struct fields use?

abu Rust Forum
#1

Coming from C, I expected one string type. In Rust, let a = "hello"; gives a &str and let b = String::from("hello"); gives a String, and the compiler rejects passing one where the other is expected with a mismatched types error. So far I have been adding .to_string() and & until it compiles.

What is the actual difference in memory between the two, and is there a rule for which type to use in function parameters, return values and struct fields?

Community replies 5

Re: Rust String vs &str: which one should function parameters and struct fields use?

#2

String is an owned, growable, heap-allocated buffer of UTF-8 bytes. The value itself is three words: pointer, length and capacity, 24 bytes on a 64-bit target. When it goes out of scope the heap buffer is freed.

&str is a borrowed view: two words, pointer and length, referring to UTF-8 bytes owned by something else. That owner can be a String, or the program's read-only data in the case of a literal, which is why a literal has the type &'static str. In C terms, String is a malloc'ed buffer you own and must free, and &str is a const char * plus a length that you must not outlive. Neither is NUL-terminated.

Re: Rust String vs &str: which one should function parameters and struct fields use?

#3

For parameters, take &str when the function only reads the text. A &String converts to &str automatically through deref coercion, so fn log(msg: &str) accepts log("literal"), log(&owned) and log(&owned[2..5]) without allocating. fn log(msg: &String) accepts only the second and forces a caller holding a literal to allocate.

Take String by value when the function needs to keep the text, for example to store it in a struct or send it to another thread; the caller then decides whether to move an existing string in or clone one. Return String when you build new text, and &str only when the result is a slice of an input, as with trimming.

Re: Rust String vs &str: which one should function parameters and struct fields use?

#4

For struct fields, default to String. A struct that holds a &str needs a lifetime parameter, struct Config<'a> { name: &'a str }, and cannot outlive the buffer it borrows from. That becomes painful as soon as the struct is returned from the function that read the data. Borrowed fields suit short-lived views, such as parser tokens pointing into the input.

On conversions: going from &str to String allocates and copies, and .to_string(), .to_owned(), String::from(s) and .into() all do the same thing. Going from String to &str is free: &s or s.as_str().

Re: Rust String vs &str: which one should function parameters and struct fields use?

#5

Something that surprises C programmers: a string cannot be indexed with an integer. s[0] does not compile, because the data is UTF-8 and one character may occupy 1 to 4 bytes. s.len() is the length in bytes, so "µA".len() is 3, since µ takes two bytes.

Slicing uses byte ranges, &s[0..2], and panics at run time if a boundary falls inside a character. Use s.chars() to iterate over characters, s.as_bytes() for the raw bytes, and s.get(0..2) for a slice that returns an Option instead of panicking.

Re: Rust String vs &str: which one should function parameters and struct fields use?

#6

For embedded no_std work the distinction matters even more. &str lives in core and needs no allocator, so literals, slicing and formatting through core::fmt::Write all work on bare metal. String lives in alloc and requires a global allocator. On small microcontrollers the usual substitute is a fixed-capacity string such as String<N> from the heapless crate, which stores up to N bytes inline, or formatting straight into a UART writer.

When a function sometimes returns its input unchanged and sometimes a modified copy, Cow<str> from std::borrow lets it return either without always allocating.

TEP COMMUNITY