ProgrammingBackend Developer

What are the features of working with constant strings and dynamic strings (String, &str) in Rust? What difficulties arise when using and converting them?

Pass interviews with Hintsage AI assistant

Answer.

Background

In Rust, there are two main string types – &str (slice of a string, immutable, often a string literal) and String (dynamic, mutable string). Early in the language's development, the choice between them simplified working with efficient memory and ensured type safety when handling textual data due to a strict ownership and borrowing system.

The Problem

Many developers get confused when interacting between these types. For example, a string literal is &'static str, which is a reference to an immutable string allocated at compile time, while String can dynamically grow and contain data obtained at runtime. Questions arise about how to convert between types, correctly use ownership, and avoid unnecessary copying.

The Solution

Conversion between &str and String is straightforward if you understand the basic rules of ownership:

  • You can get a slice from String via a reference (my_string.as_str()) or simple borrowing (&my_string).
  • You can convert &str to String using to_string() or String::from().
  • Ownership and mutability determine whether a string can be modified or needs to be cloned.

Code Example:

fn main() { let s_literal: &str = "hello"; let s_string: String = String::from(s_literal); let s_slice: &str = &s_string; let new_string = s_slice.to_string(); println!("{} {}", s_string, new_string); }

Key Features:

  • &str does not take up heap memory, always immutable.
  • String dynamically allocates memory and can be modified.
  • Simple conversion with a clear understanding of ownership and borrowing rules.

Trick Questions.

Can you modify a string literal in Rust?

No, a string literal (&'static str) is always immutable, any attempt to change a character will lead to a compilation error.

Is calling .to_string() on &str enough to get a mutable string without extra copies?

No, .to_string() always allocates new memory and copies the content. This is inevitable if a mutable string based on a slice is needed.

Can you obtain a &str reference from String without risking a lifetime leak?

Yes, the reference obtained this way (let s: &str = &my_string;) lives no longer than the original String. Attempting to return &str from a local String in a function will trigger a lifetime error.

Common Mistakes and Anti-Patterns

  • Keeping a &str reference to a temporary String that goes out of scope
  • Converting &str to String every time without necessity (unnecessary allocations)
  • Expecting String::from("text") not to create a copy of data

Real-life Example

Negative Case

Function returns &str referring to a temporary String inside the function:

fn faulty() -> &str { let s = String::from("Oops"); &s // lifetime error! }

Pros:

  • Looks simple

Cons:

  • Does not compile, violates lifetime rules
  • Possible leak of a reference to already destroyed memory

Positive Case

Function immediately returns String and transfers ownership to the caller:

fn correct() -> String { String::from("Safe!") }

Pros:

  • No lifetime issues
  • Code is reliable

Cons:

  • It may be more memory-intensive than passing a reference if the string is large and there is no need to own it.