Reputation: 1380
I have a function which selects a different array based on whether a boolean is set to true or false, similar to the following:
const V1: [u8; 2] = [1,2];
const V2: [u8; 4] = [1,2,3,4];
fn test(b: bool) {
let v = if b { &V1 } else { &V2 };
}
fn main() {
test(false);
}
However I get the following error:
error[E0308]: `if` and `else` have incompatible types
--> src/main.rs:5:33
|
5 | let v = if b { &V1 } else { &V2 };
| --- ^^^ expected an array with a fixed size of 2 elements, found one with 4 elements
| |
| expected because of this
|
= note: expected type `&[u8; 2]`
found reference `&[u8; 4]`
I tried storing the constants as vectors, but to_vec
cannot be used for constants.
An alternative would be to copy the array into a vector inside test
, but I'd rather not have to make copies every time.
Is there a way to do this without copying the array every whenever the function is called?
Upvotes: 2
Views: 932
Reputation: 709
Rust must know the type and lifetime of all variables at compile time; in this case, you are using the [u8]
type, which is a slice type for u8
elements. Slices are stored as a reference to the first element as well as the number of elements. For example, your V1
slice stores u8
elements and there are 2 of them and V2
stores u8
elements and there are 4 of them. But note that these two are not the same type because of the difference in the number of elements.
So, if you'd like to return a borrowed value of one of the two slices (V1
or V2
) you are able to do so as long as the compiler has two pieces of information; the type and their lifetime. We know that the compiler can figure out the type of both V1
and V2
since it is explicitly declared and they both live in static memory (data is part of the program source), so all we have to do is say that we are returning a reference to a slice (borrow) of u8
s and they will be around for as long as the program is running (static
lifetime). And even though V1
and V2
are not the same type, they look the same when you borrow them since all we are saying is that the return value references a bunch of u8
elements and we leave it up to the compiler to make sure it knows the number of elements for each at compile time. Check out the working example below.
const V1: [u8; 2] = [1,2];
const V2: [u8; 4] = [1,2,3,4];
fn test(b: bool) -> &'static [u8] {
let v: &[u8] = if b { &V1 } else { &V2 };
v
}
fn main() {
println!("{:?}", test(false));
}
As a final note, when attempting to solve these problems, don't be afraid to make mistakes; the compiler is actually quite friendly and very helpful when trying to figure out what to do next as shown in the error message below.
|
6 | fn test(b: bool) -> &[u8] {
| ^ help: consider giving it an explicit bounded or 'static lifetime: `&'static`
|
= help: this function's return type contains a borrowed value with an elided lifetime, but the lifetime cannot be derived from the arguments
Upvotes: 0
Reputation: 5348
The answer is to use slices, but unfortunately the rust type inference isn't clever enough to realize that. If you annotate the type of v explicitly as an &[u8]
then everything should compile.
const V1: [u8; 2] = [1,2];
const V2: [u8; 4] = [1,2,3,4];
fn test(b: bool) {
let v: &[u8] = if b { &V1 } else { &V2 };
}
fn main() {
test(false);
}
Upvotes: 4