Skip to content

RFC: Extend and stabilize the FixedSizeArray trait #1915

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 1 commit into from
Closed
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
146 changes: 146 additions & 0 deletions text/0000-array-trait.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,146 @@
- Feature Name: array-trait
- Start Date: 2017-02-21
- RFC PR:
- Rust Issue:

# Summary
[summary]: #summary

Extend and stabilize the `FixedSizeArray` trait,
as a stop-gap solution for integer parameters in generics.


# Motivation
[motivation]: #motivation

One of Rust’s basic families of types is fixed-size arrays:
`[T; N]` where `T` is any (statically-sized) type, and `N` is an (`usize`) integer.
Arrays with the same `T` item type but of different sizes are themselves different types.

A [long-standing](https://github.com/rust-lang/rfcs/pull/884)
feature [request](https://github.com/rust-lang/rfcs/issues/1038)
it the ability to write code that is generic over the size of an array.
The solution typically proposed for this it “type-level integers”,
where generic items can have integers as a new *kind* of type-level parameters,
in addition to lifetime parameters and type parameters.

However, every RFC on the subject so far has been postponed:
[1](https://github.com/rust-lang/rfcs/pull/884),
[2](https://github.com/rust-lang/rfcs/pull/1062),
[3](https://github.com/rust-lang/rfcs/pull/1520),
[4](https://github.com/rust-lang/rfcs/issues/1557),
[5](https://github.com/rust-lang/rfcs/pull/1657).

This RFC propose an addition to the language that,
although less theoretically pleasing,
is much smaller and therefore hopefully more likely to be accepted.
Additionally, it does not prevent a more general solution from also happening in the future.

Arrays are the only case where integers occur in types, in current Rust.
Therefore a solution specifically for arrays
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just handling arrays doesn't enable things like C++'s std::chrono, so it's definitely not "all". Maybe scope the claim to "std's current trait implementations for arrays" or something?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you explain what C++’s std::chrono is and how it relates to arrays?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

On re-reading you comment it sounds like it doesn’t involve arrays, but it might involve type-level integers? Yes, this RFC does not entirely remove the need for type-level integers, but it doesn’t prevent adding them later either.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm, apparently I put this comment on a poor choice of line; I thought it had the next one in the context too.

I agree with your comment, just think the motivation is over-selling itself by talking about covering "many (all?) of the use cases for type-level integers". Probably an ignorable nitpick.

Hmm, are associated constants const enough to be used in type checking? Would this enable fn pop_array<T:Array>(x : T) -> [T::Item; T::LENGTH - 1] { ... }?

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I asked this up thread and @eddyb answered : #1915 (comment) This now works when T is some fixed type, but it does not yet work if T is a type parameter like in your question.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Where "now" is whenever rust-lang/rust#40008 gets merged.

hopefully covers many of the use cases for type-level integers.


# Detailed design
[design]: #detailed-design

The `core::array` module currently contains a `FixedSizeArray` trait.
Both are unstable.
This RFC proposes three changes:

* Extend the `FixedSizeArray`. (This is optional, see alternatives.)
* Reexport the module as `std::array`.
* Stabilize both the module an the trait.

The current definition of the trait is:

```rust
pub unsafe trait FixedSizeArray<T> {
/// Converts the array to immutable slice
fn as_slice(&self) -> &[T];
/// Converts the array to mutable slice
fn as_mut_slice(&mut self) -> &mut [T];
}

unsafe impl<T, A: Unsize<[T]>> FixedSizeArray<T> for A {
#[inline]
fn as_slice(&self) -> &[T] {
self
}
#[inline]
fn as_mut_slice(&mut self) -> &mut [T] {
self
}
}
```

A single `impl` applies to array of all sizes, thanks to the `Unsize` trait.
That trait is also unstable.
It exists to support for [DST coercions](https://github.com/rust-lang/rust/issues/27732),
itself a somewhat complex feature that might take some time to stabilize.

This trait is already useful as-is,
but it would be nice to extend it to include the array’s length and item type
as an associated constant
and an associated type (which replaces the type parameter):

```rust
pub unsafe trait FixedSizeArray {
// Added:
type Item;
const LENGTH: usize;

// Unchanged:
fn as_slice(&self) -> &[Self::Item];
fn as_mut_slice(&mut self) -> &mut [Self::Item];
}
```

However these can not be provided by in `impl` based on `Unsize<[T]>` like the current one.
Instead, this RFC proposes that all array types implement the trait through “compiler magic”.
There would be no corresponding `impl` block in libcore or other library crates,
the existence of these implementations would be part of the definition of the Rust language.
There is precedent for such “magic” implementations in the language of other traits:

* `Unsize`
* `Sized`
* `Fn`, `FnMut`, `FnMove` (for closures types, which are very magical themselves)
* `Copy`, `Drop` (not implemented implicitly, but `impl`s have “magical” requirements)


# How We Teach This
[how-we-teach-this]: #how-we-teach-this

The `array` module and `FixedSizeArray` trait already have rustdoc documentation
that seems appropriate.

The trait could be mentioned in the *Primitive Types* or *Generics* chapter of the book.


# Drawbacks
[drawbacks]: #drawbacks

This an ad-hoc work-around for something that could be solved with a more general mechanism
in the future.


# Alternatives
[alternatives]: #alternatives

* Stabilize the `FixedSizeArray` trait as-is
(without an associated type or associated constant, with a type parameter).

* Figure out type-level integers, and deprecate / remove the `FixedSizeArray` trait.


# Unresolved questions
[unresolved]: #unresolved-questions

* To extend or not to extend. (See [Alternatives](#alternatives).)

* Should the compiler prevent other implementations of the trait?
There is precedent for that with the `Unsize` trait and error number E0328.

* Rename `FixedSizeArray` to `Array`?
All arrays in Rust are fixed-size.
The things similar to arrays that are not fixed-size are called slices or vectors.