43

I'm trying to manipulate a string derived from a function parameter and then return the result of that manipulation:

fn main() {
    let a: [u8; 3] = [0, 1, 2]; 
    for i in a.iter() {
        println!("{}", choose("abc", *i));
    }
}

fn choose(s: &str, pad: u8) -> String {
    let c = match pad {
        0 => ["000000000000000", s].join("")[s.len()..],
        1 => [s, "000000000000000"].join("")[..16],
        _ => ["00", s, "0000000000000"].join("")[..16],
    };
    c.to_string()
}

On building, I get this error:

error[E0277]: the trait bound `str: std::marker::Sized` is not satisfied
 --> src\main.rs:9:9
  |
9 |     let c = match pad {
  |         ^ `str` does not have a constant size known at compile-time
  |
  = help: the trait `std::marker::Sized` is not implemented for `str`
  = note: all local variables must have a statically known size

What's wrong here, and what's the simplest way to fix it?

Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
Brent.Longborough
  • 9,567
  • 10
  • 42
  • 62

1 Answers1

55

TL;DR Don't use str, use &str. The reference is important.


The issue can be simplified to this:

fn main() {
    let demo = "demo"[..];
}

You are attempting to slice a &str (but the same would happen for a String, &[T], Vec<T>, etc.), but have not taken a reference to the result. This means that the type of demo would be str. To fix it, add an &:

let demo = &"demo"[..];

In your broader example, you are also running into the fact that you are creating an allocated String inside of the match statement (via join) and then attempting to return a reference to it. This is disallowed because the String will be dropped at the end of the match, invalidating any references. In another language, this could lead to memory unsafety.

One potential fix is to store the created String for the duration of the function, preventing its deallocation until after the new string is created:

fn choose(s: &str, pad: u8) -> String {
    let tmp;

    match pad {
        0 => {
            tmp = ["000000000000000", s].join("");
            &tmp[s.len()..]
        }
        1 => {
            tmp = [s, "000000000000000"].join("");
            &tmp[..16]
        }
        _ => {
            tmp = ["00", s, "0000000000000"].join("");
            &tmp[..16]
        }
    }.to_string()
}

Editorially, there's probably more efficient ways of writing this function. The formatting machinery has options for padding strings. You might even be able to just truncate the string returned from join without creating a new one.


What it means is harder to explain succinctly. Rust has a number of types that are unsized. The most prevalent ones are str and [T]. Contrast these types to how you normally see them used: &str or &[T]. You might even see them as Box<str> or Arc<[T]>. The commonality is that they are always used behind a reference of some kind.

Because these types don't have a size, they cannot be stored in a variable on the stack — the compiler wouldn't know how much stack space to reserve for them! That's the essence of the error message.

See also:

Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
  • 1
    Than you. Special additional thanks for the pointer to string padding, which I'll investigate shortly. A language like Rust or Python, with it's lovely, vast, almost-built-in libraries, is a slightly two-edged sword. Still got a lot to learn... – Brent.Longborough Mar 21 '18 at 11:57
  • 2
    "What it means is harder to explain succinctly." Then goes on to explain it succinctly. – AndreKR Mar 10 '20 at 13:15
  • @AndreKR Thanks (I think)! However, that part of the answer is pretty weak, as it doesn't really explain *how* something can be "unsized" or why such a concept is useful, not to mention glossing over what the stack is. – Shepmaster Mar 10 '20 at 13:25
  • I think most languages store their local variables on a stack, so it's a familiar concept. And why it's unsized is in the error message - it doesn't implement `Sized`. I just didn't make the connection until I read your post. – AndreKR Mar 10 '20 at 13:36
  • 1
    Further explanation why we can't use str instead of &str: https://users.rust-lang.org/t/str-size-at-compile-time/76970/2 – Crispy13 Jan 09 '23 at 12:14