HiveBrain v1.2.0
Get Started
← Back to all entries
debugrustMajor

What does “`str` does not have a constant size known at compile-time” mean, and what's the simplest way to fix it?

Submitted by: @import:stackoverflow-api··
0
Viewed 0 times
constantknownfixcompilehaveandtimestrthedoes

Problem

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?

Solution

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, 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 or Arc. 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:

  • What is the return type of the indexing operation?



  • Return local String as a slice (&str)



  • Why your first FizzBuzz implementation may not work

Code Snippets

fn main() {
    let demo = "demo"[..];
}
let demo = &"demo"[..];
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()
}

Context

Stack Overflow Q#49393462, score: 65

Revisions (0)

No revisions yet.