I make this thread motivated by these conversations
Proposal
Add a array type [T;A..B]
meaning an array of elements of type T
with size in the range A..B
. And let coercions from the array [T;N]
when N
is in the range. This would allow to write functions that aim to return arrays of different sizes. And I think it would be a more conservative approach than returning the unsized [u8]
as proposed in the other threads.
fn foo(input:bool) -> [u8;4..6]{
if input{
[1,2,3,4,5]
} else {
[2,4,6,8]
}
}
It would also help to write Cow
s to to arrays with a known upper bound on their size.
fn foo_cow<'a>(input:bool, else_ref:&'a [u8;4..6]) -> Cow<'a,[u8;4..6]>{
if input{
Cow::Owned([1,2,3,4,5])
} else {
Cow::Borrowed(else_ref)
}
}
Then a proper Return Value Optimization could create these data directly in the caller stack, at discretion of the compiler.
Issues
Its implementation would require to store the size. I think the [T;N]
contains just the N
elements and that &[T]
is a size plus pointer. This puts [T;A..B]
in a awkward position, as it would store B-A+1
elements plus the size. And then &[T;A..B]
would be a single pointer. So both conversions [T;N]
into [T;A..B]
and &[T;A..B]
into &[T]
would require a little work.
Notation and alternatives
We could admit to use [T;A..=B]
. It seems way more useful the upper limit, so we may want to omit A
, allowing always to include 0-sized arrays. We could write this as [T;..=B]
.
Even a type without the coercions from [T;N]
could be useful, although it would be more verbose. For example it would be nice to be able to define SliceUpTo<N>
for arrays of length at most N
.
Const-generics
This would seem to have much relation with the const-generics feature, which I am not sure in what state is currently implemented. It almost seem that we should be able to implement it as user. But I find myself failing to implement it as follows.
struct SliceUpTo<T,const N:usize>{
size: usize,
data: [T;N],//And just use some part via transmutes?
}
impl<T,const N:usize> SliceUpTo<T,N>{
fn new<M:usize>(value:[T;M]) -> SliceUpTo<T,N>
where M <= N // <-- We cannot write this
{ todo!(); }
fn into_array(self) -> [T;M] // <-- This should require M=self.size, which is way too much.
{ todo!(); }
fn as_ref(&self) -> &[T] // <-- Although perhaps `into_array` is not required and just implement `as_ref`.
{ todo!(); }
fn as_mut(&mut self) -> &mut [T]
{ todo!(); }
}
Final comment
Question for the authors of the two linked threads. Would this approach be satisfying enough for your use-cases?