0

I'm still new to rust, so I'm not sure if this is phrased correctly, but essentially, I'm looking for a reason why rust doesnt understand that this array has, and only ever will have 3 elements at compile time? I can simply add a 3 to the type, and it works. or i change it to borrowed &[&'static str] (which i assume majorly changes how it compiled), and it will also work. What am I missing?

const strings: [&'static str] = ["Element 1", "Element 2", "Element 3"];
// the size for values of type `[&'static str]` cannot be known at compilation time
// the trait `Sized` is not implemented for `[&'static str]`
Valerie Thiesent
  • 891
  • 1
  • 8
  • 17

3 Answers3

4

[&'static str] is a slice of &'static str elements. A slice is kind of like an array, but has runtime-defined length – and you can't really hold something with a length that is unknown at compile-time inside a variable as this variable must have a defined length.

As you already found out, you have two possible solutions:

1.

const strings: [&'static str; 3] = ["Element 1", "Element 2", "Element 3"];

This constructs a variable of the type "array of &'static str, length 3". The length of the array is part of the type information.

2.

const strings: &[&'static str] = &["Element 1", "Element 2", "Element 3"];

In this version, strings only holds a reference to a slice holding the elements. How does this reference work, and how is it constructed?

  1. References to slices are actually not normal references, but fat pointers consisting of a pointer to the first element of the slice combined with the slice length (see for example this for more information about fat pointers in Rust).
  2. Here, Rust first builds an array with length 3 and then borrows it, thereby becoming a &[&'static str]. (As you might have noticed, borrowing actually gets you a &[&'static str; 3] – Rust implicitly converts this from "reference to array" to "reference to slice".)

Which solution should you use?

In most cases, it doesn't really matter. Personally, I'd use version 1 and convert to a slice when needed, thereby preventing the (small) overhead of the fat pointer if possible.

Elias Holzmann
  • 3,216
  • 2
  • 17
  • 33
  • This is all really helpful, though I suppose it makes my initial question more clear. It makes sense, then, why the size needs to exist, but why can the compiler not see 3 elements in the literal, and infer the type to be of size 3? Is that simply a limitation, or does it have a root in potential problems it could cause? From what you've said here, I imagine the answer to be along the lines of "[x], [x; 3] and [x; 5] are all fundamentally different types, and the compiler will not overwrite an explicit type with inference, as [x] is a valid type, just not for this value." – Valerie Thiesent May 27 '23 at 14:27
  • 2
    @ValerieThiesent actually, the reason is that `[T]` is a distinct type from `[T; N]` (for any `N`), so if you explicitly tell the compiler "forget about the size of the array", it will do so, and then it will complain that it should not do so. There is a way tell the compiler to figure out the size of the array on its own, which is to use `[T; _]` (that is, "this value has a type `[T; N]` but I want you (the compiler) to figure it out for me"). I'm not sure whether this is currently allowed in `const` types, though. – jthulhu May 27 '23 at 15:25
  • 1
    @ValerieThiesent For most use cases, Rust can not only infer the size 3, but also the information that it is an array. However, const variables do not support type inference at all – you need to supply the (complete) type of the variable. See [this playground](https://play.rust-lang.org/?version=nightly&mode=debug&edition=2021&gist=c0712db5e0f5e26e1df7c929b032e6bd) for an example. This is indeed a somewhat unnecessary limitation of Rust, see [here](https://stackoverflow.com/a/55531693) for more information about the reasons of this limit. – Elias Holzmann May 27 '23 at 15:31
  • @jthulhu Thank you, I didn't know about `[T; _]` yet! Seems to be unstable for now (behind `#![feature(generic_arg_infer)]`) and to not support `const` currently, but interesting nonetheless. – Elias Holzmann May 27 '23 at 15:37
0

In Rust arrays have a fixed size that is determined at compile time. The size of an array is part of its type, which means that arrays with different sizes are considered to have different types. In your case, the array const strings: [&'static str; 3] has a size of 3, and this size is known at compile time.

However, when you use a slice ([&'static str]) instead of an array, the size of the slice is not known at compile time. Slices are dynamically sized and can represent a view into a contiguous sequence of elements, which can vary in length. To resolve this issue you can change the type of strings from an array ([]) to a vector (Vec<&'static str>) which can dynamically adjust its size at runtime.

const STRINGS: &[&'static str] = &["Element 1", "Element 2", "Element 3"];

fn main() {
   // Convert the slice to a vector
    let strings: Vec<&'static str> = STRINGS.to_vec();
     
    println!("{:?}", strings);
}
misar
  • 11
  • 5
0

In Rust, arrays have a fixed size that is known at compile time. When you declare a constant array like const strings: [&'static str] = ["Element 1", "Element 2", "Element 3"];, the compiler needs to determine the size of the array at compile time. However, in this case, the compiler cannot determine the size because the type [&'static str] represents a dynamically sized type.

The type [&'static str] is an array of references to string slices with the 'static lifetime. Each string slice can have a different length, so the overall size of the array cannot be known at compile time. This is why the compiler gives you an error stating that "the size for values of type [&'static str] cannot be known at compilation time."

TopTickTom
  • 21
  • 2