How do you do a string slice in a const function?
const fn cya_first_char(inc: &str) -> &str {
&inc[1..]
// ~~~~~ Error: cannot call non-const operator in constant functions
}
str
doesn't have too many const methods, but as_bytes
is, and [u8]
has a const split_at
method. Then you can turn that back into str
with std::str::from_utf8
.
const fn take_first_bytes_of_str(s: &str, i: usize) -> &str {
let Ok(s) = std::str::from_utf8(s.as_bytes().split_at(i).1) else {
panic!("first character was more than one byte");
};
s
}
const fn cya_first_char(inc: &str) -> &str {
take_first_bytes_of_str(inc, 1)
}
This will scan the entire rest of the string for validity every time, so it will detrimentally affect performance if called on long strings or when called many times. If that's what you need, then it'll probably be worth reimplementing is_char_boundary
in const and using that with from_utf8_unchecked
.
Here is one that takes the first character instead of the first byte.
const fn take_first_char_of_str(s: &str) -> &str {
let mut i = 1;
while i < 5 {
let (first, rest) = s.as_bytes().split_at(i);
if std::str::from_utf8(first).is_ok() {
// SAFETY: if `first` is valid UTF-8, then `rest` is valid UTF-8
unsafe {
return std::str::from_utf8_unchecked(rest);
}
}
i += 1;
}
unreachable!()
}
const fn cya_first_char(inc: &str) -> &str {
take_first_char_of_str(inc)
}
This one gets around the performance issue by only checking the first character for validity, and assuming the rest. This way, it only has to check at most 1 + 2 + 3 + 4 = 10 bytes for any length of string.
This is still not ideal, but will run in constant time.