Hi there,
if i write
let s1: str = "hi";
compiler complains:
the size for values of type str
cannot be known at compilation time. OK.
but 'h' + 'i' has a well defined size.
So why compiler is unable to evalute the size of "hi"?
Thx.
Hi there,
if i write
let s1: str = "hi";
compiler complains:
the size for values of type str
cannot be known at compilation time. OK.
but 'h' + 'i' has a well defined size.
So why compiler is unable to evalute the size of "hi"?
Thx.
It’s a fundamental property of the str
type that you can’t ever store one directly in a variable. Instead, it can only exist as part of another type, like &str
, Box<str>
, or (implicitly) String
.
The compiler error message is summarizing why str
is designed this way, even though it might not be literally true in all cases. You probably want either:
let s1: &str = "hi"; // If the string won’t be edited
or
let mut s1 = String::from("hi"); // If you need to edit the string later
To be clear, the compiler is not trying to evaluate the size of "hi". A variable with type str
needs to be able to store any possible value of that type, including strings that are two gigabytes long. Because of this, a variable cannot have the type str
.
In this particular case, we should add that "hi"
doesn't even have the type str
. It has the type &str
. That's because your code more or less compiles down to this:
static HI_DATA: [u8; 2] = [b'h', b'i'];
let s1 = std::str::from_utf8_unchecked(&HI_DATA);
The thing to notice is that s1
doesn't actually store the string data. It just has a pointer to an immutable global storing the string data.
(the from_utf8_unchecked
call converts reference type from &[u8]
to &str
)
If you want a variable that actually stores the string data directly, you can do this:
let s1 = [b'h', b'i'];
This gives s1
the array type [u8; 2]
. You can get a string slice into that string data using std::str::from_utf8
.
Note that the error message is (as you say):
error[E0277]: the size for values of type `str` cannot be known at compilation time
It says, "values of type str
". It doesn't refer to the particular value "hi"
, but to values of str
in general.
When you declare a variable s1: str
in your code, then the compiler will not know how much space to reserve for it.
Rust doesn't have a type for owned string slices of a fixed size, but it does know fixed sized arrays. So what you could do is this:
const CHARS: [char; 2] = ['h', 'i'];
This doesn't make much sense though, as it won't be an UTF-8 string but an array of 32-bit integers internally.
Nonetheless, you could work with this fixed-size array of chars (but note that this is not what you're supposed to do in Rust normally):
fn main() {
const CHARS: [char; 2] = ['h', 'i'];
for c in &CHARS {
print!("{c}");
}
println!("");
}
Output:
hi
Instead, the correct way to do it is this:
fn main() {
const S1: &'static str = "hi";
println!("{S1}");
}
Output:
hi
You could also do:
fn main() {
let s1: &'static str = "hi";
println!("{s1}");
}
Or shorter:
fn main() {
let s1 = "hi"; // implicit type: &'static str
println!("{s1}");
}
Honestly, I'm not sure when it's best to use let
and when to use const
. It confuses me sometimes. When you use const
, you will have to specify the type explicitly and you should use all uppercase letters for the name.
const
, that almost always means you need let
.#define
, that means you need const
(or macros).Even better, you can move out of a byte string literal to achieve the same effect in a slightly more ergonomic way:
let s1 = *b"hi";
A let
binding (conceptually at least—the optimiser may cause otherwise) occupies some memory on the stack; whereas a const
is not stored anywhere (the compiler essentially replaces every usage with its value).
Prefer a const
whenever the value is known at compile-time. You have to use let
if the value may not be known until runtime.
Thx you all for your answers. All this is really instructive in relation to a somewhat anomalous topic for those who come from certain other languages (me, for example)