---
title: Error: "expected &str, found String" — How to Fix
summary: Fix the 'expected &str, found String' error by converting the String to a slice with .as_str() or passing a reference with &.
lang: en
slug: error-expected-str-found-string-how-to-fix
published: 2026-04-17T14:12:14+00:00
updated: 2026-04-25T22:00:13+00:00
canonical: https://www.rustfaq.org/en/error-expected-str-found-string-how-to-fix/
---

# Error: "expected &str, found String" — How to Fix



## The mismatch that stops you cold

You write a function to parse a configuration line. It takes a `&str`. You read a file and get a `String`. You pass the string to the function. The compiler halts with E0308 (mismatched types). It feels like Rust is being pedantic about a type that looks identical to the one you have.

The compiler is not guessing. It is refusing to decide whether you want to lend the data or give it away. `String` and `&str` are fundamentally different concepts in Rust's type system. One owns the memory. The other is a view into memory owned by someone else. Confusing them leads to accidental copies, dangling pointers, or ownership transfers you didn't intend. Rust forces you to make the choice explicit.

## View versus container

Think of `String` as a potted plant you bought at a nursery. You own the pot, the soil, and the plant. You can move it to a new shelf, give it to a friend, or throw it away. You are responsible for its lifecycle.

`&str` is a photograph of that plant. The photo shows you the plant, but you don't own the plant. You can hand the photo to anyone. They can look at it. They cannot water the plant through the photo. If you throw away the real plant, the photo becomes useless. The photo must never outlive the plant.

In Rust, `String` is the potted plant. It allocates a buffer on the heap and owns it. `&str` is the photograph. It is a "fat pointer" containing a memory address and a length. It points into a buffer owned by a `String`, a string literal, or another source. It borrows the data. It cannot modify the buffer, and it cannot extend the buffer.

This distinction is structural. The compiler treats them as different types because they carry different guarantees. A function that takes `&str` promises not to take ownership. It promises the data will still be there when it returns. If Rust allowed you to pass a `String` directly, it would have to decide whether to borrow the string or move it. Moving the string would leave the caller with nothing, which is often a bug. Borrowing is safer, but Rust prefers explicit borrows so you can see the lifetime relationships in your code.

## Memory layout: why they differ

The difference goes deeper than semantics. The in-memory representation of these types is different.

A `String` is a struct with three fields: a pointer to the heap buffer, the current length, and the capacity. Capacity is the amount of allocated space, which may be larger than the length to allow for efficient growth. When you push characters to a `String`, it uses the spare capacity. If it runs out, it allocates a new, larger buffer and copies the data.

A `&str` is a slice type with only two fields: a pointer and a length. It has no capacity. You cannot grow a `&str`. It is a fixed view. This layout difference is why they are distinct types. A `String` can mutate its capacity. A `&str` cannot. If you pass a `String` where a `&str` is expected, the compiler needs to know whether to extract the pointer and length (creating a view) or to hand over the whole struct (transferring ownership).

```rust
fn analyze(text: &str) {
    // text is a view. It has ptr and len.
    // It cannot grow. It cannot be mutated.
    println!("Length: {}", text.len());
}

fn main() {
    // owned is a container. It has ptr, len, and capacity.
    // It owns the heap buffer.
    let owned = String::from("Rust is safe");
    
    // Error: expected &str, found String.
    // The compiler sees a struct with 3 fields vs a slice with 2 fields.
    // analyze(owned);
    
    // Fix: create a view from the container.
    analyze(&owned);
}
```

## The fix: borrow or convert

You have two standard ways to resolve this error. Both are correct. The choice depends on what you want to communicate.

The first approach is to add a reference. Prefix the variable with `&`. This creates a `&String`. Rust then applies a mechanism called deref coercion to convert `&String` into `&str` automatically. This is the most common fix. It signals that you are borrowing the string and the owner remains valid.

The second approach is to call `.as_str()`. This method returns a `&str` explicitly. It extracts the pointer and length from the `String` and hands you a slice. This is useful when you need to be explicit about the type, or when you are chaining methods and want to ensure the result is a slice.

```rust
fn greet(name: &str) {
    // name is borrowed. The caller keeps ownership.
    println!("Hello, {}", name);
}

fn main() {
    let owned_name = String::from("Alice");
    
    // Borrow the string. &owned_name is &String.
    // Deref coercion converts &String to &str.
    greet(&owned_name);
    
    // Explicitly extract a slice.
    // Returns &str directly.
    greet(owned_name.as_str());
    
    // owned_name is still valid. We didn't move it.
    println!("Name is still {}", owned_name);
}
```

## Deref coercion: the hidden helper

The reason `&owned_name` works even though the type is `&String` is deref coercion. `String` implements the `Deref` trait with a target of `str`. When the compiler sees a reference to a type that implements `Deref`, it can automatically dereference it to get a reference to the target type.

This coercion happens transparently in function arguments and method calls. It allows `&String` to flow into parameters expecting `&str`. It also allows you to call `&str` methods on `&String`. For example, `owned_name.len()` works because `String` derefs to `str`, and `str` has the `len` method.

This coercion is a convenience, not a type equality. `&String` and `&str` are still different types. You cannot assign a `&String` to a variable typed as `&str` without coercion. The compiler handles the conversion at the call site.

## Realistic scenario: processing config

In real code, you often read data from files or networks, which gives you `String`s. You then pass that data to functions that process slices. The pattern is consistent: read into a `String`, borrow slices from it.

```rust
use std::fs;

/// Validates a config key. Takes a slice to avoid ownership.
fn is_valid_key(key: &str) -> bool {
    key.starts_with("cfg_") && key.len() > 4
}

fn main() {
    // Read the whole file into an owned String.
    let content = fs::read_to_string("config.txt").unwrap();
    
    // Iterate over lines. lines() yields &str slices.
    // Each slice borrows from content.
    for line in content.lines() {
        // line is &str. No conversion needed.
        if is_valid_key(line) {
            println!("Found key: {}", line);
        }
    }
    
    // Check a dynamic override stored as a String.
    let override_key = String::from("cfg_debug");
    
    // Borrow the override to pass to the function.
    if is_valid_key(&override_key) {
        println!("Override active");
    }
    
    // content and override_key are still owned by main.
    // They drop when main ends.
}
```

## Pitfalls and compiler signals

The error E0308 appears whenever types don't match. In this context, it means you passed a `String` where a `&str` is expected. The compiler message will show "expected `&str`, found struct `String`".

A related pitfall is lifetime errors. If you try to return a `&str` derived from a local `String`, the compiler rejects it with E0515 (cannot return value referencing local variable). The slice must not outlive the owner.

```rust
fn bad_example() -> &str {
    let local = String::from("temporary");
    // Error: E0515. local is dropped at end of function.
    // The &str would point to freed memory.
    local.as_str()
}
```

Another pitfall is assuming `&String` and `&str` are interchangeable everywhere. They are not. You can pass `&String` to a function expecting `&str` due to coercion. You cannot pass `&str` to a function expecting `&String` because a slice doesn't have capacity information. The coercion is one-way.

## Decision matrix

Use `&variable` when you have a `String` and the function takes `&str`. The borrow creates a `&String`, which Rust automatically coerces to `&str` via deref coercion. This is the idiomatic choice for most cases.

Use `.as_str()` when you need an explicit `&str` type, such as when assigning to a variable typed as `&str`, or when chaining methods where you want to emphasize the slice extraction.

Change the function parameter to `String` when the function must take ownership of the data, such as storing it in a struct, moving it into a thread, or consuming it to avoid a copy.

Reach for `Cow<str>` when writing a library function that accepts string-like data and you want to avoid forcing the caller to allocate a `String` if they only have a `&str`. `Cow` stands for "clone on write" and accepts either owned or borrowed strings.

Prefer `&str` in your function signatures unless you specifically need ownership. This convention makes your API flexible. Callers can pass string literals, slices, or borrow their `String`s without allocation. It is the standard pattern in the Rust ecosystem.

Borrow the string. Keep the owner alive. The compiler protects you from dangling views by enforcing this rule at compile time.

## Where to go next

- [How to convert between String and str](/en/how-to-convert-between-string-and-str/)
- [What is the Cow str type](/en/what-is-the-cow-str-type/)
- [How to Handle Different Number and Date Formats in Rust](/en/how-to-handle-different-number-and-date-formats-in-rust/)