Rust String vs &str
Tags: rust
Published: 2020-10-10 13:22:45

"Why does Rust have different types of Strings? Why not just one String type?"

I have seen these questions posed in different ways online and it is true that Rust's `String` and `&str` types can be confusing for people new to Rust, especially people who are familiar with dynamic programming languages. So let's talk about the two string types and see if we can clear up some of the confusion.

It will make sense eventually and Rust really does need both types. They are quite different and have different use cases.

## TL;DR

It's all about "who owns the memory".

#### String 

* `String` contains a string in memory and owns the memory for it.

* Use `String` for returning strings created within a function or (usually) when storing strings in a struct or enum.

* If you have a `String` you can pass a reference to it to convert it to `&str`.

#### &str

* `&str` is just a reference to another string (slice) but does not own the memory for it.

* Prefer `&str` in function arguments to accept string slices and make it clear the function will not mutate the string.

* If you have a `&str` and want a new `String` you can clone it either by `to_owned()` or `to_string()` (they are effectively the same - use whichever makes your code clearer to read and consistent). These will copy the memory and make a new `String`.

## It's about memory and ownership

Firstly, we need to talk a little about how Rust manages memory. I'll try to keep this brief but it will be very important later when we discuss the difference between `String` and `&str`.

As you may be aware, Rust does not have a garbage collector. That means, the compiler needs to explicitly allocate and deallocate memory at specific points in the code, and it needs a mechanism for determining when something is no longer in use so that its memory can be deallocated safely.

This mechanism in Rust is called "ownership" (and "borrowing"). Consider the following code:
```rust
fn main() {
    let owned_string = get_string();
    print_string(&owned_string);
}
    
fn get_string() -> String {
    let s = String::from("Hello, World!");
    // In Rust we return s by omitting the semicolon
    s
}

fn print_string(my_string: &str) {
    println!("{}", my_string);
}

```

Here in `get_string()` we allocate memory for a String on the heap (to learn more about what the heap is, [see here](https://doc.rust-lang.org/1.22.0/book/first-edition/the-stack-and-the-heap.html)) and assign it to the variable `s`. At this point we can say that `s` "owns" that memory. The Rust compiler will now track the scope of `s` in order to know when to deallocate it. This would occur when `s` goes out of scope, unless `s` is instead "moved" (which you can think of like a transfer of ownership). In the above case, `s` is indeed moved, into the variable `owned_string` at the top.

When `get_string()` returns, the memory isn't copied and it doesn't go anywhere. Rather, the ownership of that memory is now transferred to the caller (in this case the variable `owned_string`).

The Rust compiler would now track this new owner `owned_string` in order to know when to deallocate. In this case it would be deallocated at the end of the `main()` function.

A `String` will only ever have 1 owner. That owner can allow others to "borrow" it temporarily (see the line `print_string(&owned_string);`) , but the memory will only be deallocated when the owner goes out of scope (unless it is moved, in which case it will be deallocated when the new owner goes out of scope, and so on).

# Borrowing and references

So now let's look at the other function:
```rust
fn print_string(my_string: &str) {
    println!("{}", my_string);
}
```

The `&` symbol means this is a reference to a `str` (a string slice). You will almost never use `str` without `&` so it's easier to just think of them as `&str`, or "a reference to a string slice".

Consider the `String` we had earlier with its owned, allocated memory. What if we wanted to pass a reference to that string (or part of it), but without allowing the recipient to modify it. In Rust we call that an "immutable borrow", and that is what the `&` means. It is like a "reference" in other languages, but Rust goes a step further and lets you declare whether or not its contents can be mutated (changed).

In the above example we could have used `&String` in the function signature instead of `&str` and that would have worked ok because we passed in the entire string anyway. However, that wouldn't allow other callers to pass in a substring (at least without copying its memory into another `String`). This is where a string slice `&str` is much more useful.

`&str` is effectively a pointer directly to the string's memory, with a fixed length. You will notice in the original code the line `print_string(&owned_string);`. We passed a reference to a `String` into the function, but the function wanted `&str`. This works because the `String` type can automatically convert into `&str` - this is just returning a pointer to the very same memory, with a known length. `&str` is also an immutable reference so you cannot modify the memory it points to.

In fact, Rust will ensure that the `&str` is safe to use (even across threads!) by guaranteeing that *nothing* can modify the memory it points to (even the owner), for as long as the `&` reference variable is in scope. Rust will also guarantee that the owned `String` (that the `&str` points to) cannot go out of scope while the reference remains in scope (otherwise the reference would be a dangling pointer). These are very powerful features of Rust's borrow checker that prevent all kinds of nasty bugs you might encounter with other languages where you create a string over here then pass a reference to it over there but it accidentally modifies the original.

## The difference between String and &str is...

`String` holds a string in memory and owns the memory for it.

`&str` is just a reference to another string but it doesn't own the memory for it.

## When to use either one

In general, use `String` in structs and enums where you want the struct or enum to own the contents. Also use `String` when returning a string from a function where that string was allocated within the function (Rust will not let you return a reference in that case because the memory would be deallocated at the end of the function and the reference returned would be a dangling pointer)

Prefer `&str` for function arguments unless you explicitly want to move a `String` into the function and give up ownership of it (this tends to be rare).

## One last time...

Use `String` when you need to own the memory, for example you created a string in a function and need to return it.

Use `&str` when you want an immutable reference to memory that is owned by another `String` variable (or a string literal in your code).