[Accepted with modifications] SE-0453: InlineArray (formerly: Vector, a fixed-size array)

Hello Swift community,

The second review of SE-0453: Vector, a fixed-size array concluded on December 17, 2024. The Language Steering Group has decided to accept this proposal with modifications. In addition to a couple small API changes, this type will be introduced with the name InlineArray.

API Changes

The following proposed initializer:

public init<E: Error>(with next: (Index) throws(E) -> Element) throws(E)

will drop the argument label and be introduced as init(_:)

Additionally, it was pointed out in review that the subscripts on InlineArray were different than those on Span. The Language Steering Group agreed that InlineArray should adopt the missing subscripts from Span.

On Naming

The Language Steering Group had a long discussion about the naming of this type and considered both the principles and concrete alternatives that were brought up in the review thread and via private feedback. We settled on the name InlineArray for the following reasons.

Regarding the name Vector, the Group broadly agreed with the arguments that the mismatch in meaning of 'vector' between Swift and some other peer languages like C++ and Rust would be unfortunate. We were also receptive to arguments that this was not synonymous enough with mathematical vectors to justify the name on those grounds alone. Given this, we considered what other names than Vector might be suitable for this type.

The Language Steering Group did not feel that it was inappropriate for users to conceptualize this as an 'array' type in a colloquial sense. We agreed with the argument that 'fixed-size array' accurately captured much of the behavior of this type, even considering its relationship with the potential 'array' types that we may want to support in the future.

Nonetheless, this type does differ from the vanilla Array type in ways which we felt were important to communicate in the name of the type. Top of mind for the Language Steering Group was the behavior of this type regarding copies: namely, that this type is eagerly copied rather than being copy-on-write which carries substantial performance implications. The Group felt that the Inline- prefix captured the salient aspects of how this type's storage is organized.

Many reviewers felt that FixedSizeArray was the most obvious and straightforward name for this type, and the Group was sympathetic to this argument. However, we did not feel this name (or its variants such as FixedArray) sufficiently called out the potential performance issues discussed in the prior paragraph (one could, of course, have a fixed-size array with out-of-line storage and copy-on-write behavior more like Array). Moreover, the Group feels the fixed-size nature of this array will be sufficiently apparent from both from the fact that the generic count parameter appears in the type signature, and from the fact that one will get a compiler error as soon as there's any attempt to actually use a size changing operation—the fixed-size property of this type is not something it's really possible to meaningfully 'get wrong' compared to its performance characteristics.

The Group also discussed alternative, pithy names for the type that could become a new term of art, but in the end did not find a compelling solution along those lines. In particular, each option seemed like it would invite just as much (if not more) clarification than Vector along the lines of "NewTermOfArt , an inline fixed-sized array."

As always, thank you to everyone for your thoughtful participation in these reviews. This was a long but productive discussion, and your contributions help to make Swift a better language.

Freddy Kellison-Linn
Review Manager

76 Likes

The other major difference between this type and Array, of course, is that this type must be fully occupied. You cannot have 2 elements in an InlineArray<3, Int>.

My understanding is that we do plan to introduce such a type in the near future, and just like this type, I'm certain it would certainly see plenty of use (it can be used to create a safe alternative to ManagedBuffer, for instance - a painfully unsafe and awkward API that we've all long been wanting to replace).

The natural question is what we would call that type if this is our InlineArray. Do you have any thoughts on that?

10 Likes

That would be news to me, at least...

We wouldn't want to preempt what authors and community members have in mind :slight_smile:
But suffice it to say that the group isn't worried that we'd be stumped for ideas.

1 Like

Well, it was pretty clear here:

8 Likes

I'm not confused about the idea itself; I'm saying there's been no indication that anything is or isn't going to happen in the near-future.

And certainly, nothing in this review acceptance should be construed to mean that our plan of record includes adopting or not adopting any other types.

We can quibble about precisely how "near" or "far" any of these plans are, or what duration of time consistutes "near", but what is more important to me is that the standard library maintainers pitched a name with broader consideration as to how it fit within a family of types that they intend to deliver (at some point...).

It is fine if the LSG wish to choose a different name, given the arguments from the community, but I would like to hear what your ideas are with regards to the original concerns laid out by the stdlib maintainers, which led them to choose the pitched design.

14 Likes

We talked about it a lot. It seems inevitable that we'll end up with many different types in the design space of arrays. When you need tight control over allocation and per-operation costs — whether you're writing an embedded system or just doing very careful performance work — general-purpose data structures usually need to give way to more specialized designs. We may end up with ten or more array types across the standard library and other core libraries. Hopefully, most programmers will never need to use anything besides Array, because Array will serve their needs just fine. Other programmers may need to be conversant with most or all of them.

We will not end up with some completely consistent, taxonomic naming system for all those types. If nothing else, Array will always sit outside that system: it has key implementation characteristics that will never be reflected in its name because we are not going to rename it to ShareableDynamicArray as would befit its role in the taxonomy. Moreover, predicting what characteristics are important to call out in the name is a losing game: if we did have a type named ShareableDynamicArray, what would happen in five years when we realize that we need a second shareable dynamic array type with different characteristics along a new dimension? And besides, names like these are awful: they're long, they're pedantic, and they're unapproachable for new users.

In my opinion, it is better to pick distinctive names that evoke the role of the type, and it's fine for more specialized types to have more complex names as a rough proxy for how unlikely it is that someone will need to use it. So I am not worried that we will box ourselves into a corner here. If we find that we need a fixed-capacity, dynamically-sized array with inline storage, this name will be taken, and we will have to find a different name for the new type that seems to adequately distinguish it. But that will be quite a specialized type, and it might have rather specialized operations to go with it (e.g. we might want append to fail recoverably rather than with a precondition trap); I do not think people will be casually reaching for it.

30 Likes

Yeah, I get that if you fully extend this out, that it becomes too onerous, but it seems to me like the other type is:

  1. Also Inline
  2. More Array-like than InlineArray (it's as Array-like as you can get while being inline)

Which doesn't sound right.

I mean, one common pattern that is quite annoying is when you have a few items to process, but need to conditionally include one (or a few).

var items = [a, b]
if condition { items.append(c) }
process(items)

That currently costs a heap allocation, and if the other thing was called InlineArray, you could just switch Array -> InlineArray to remove that. Yeah, you'd need to add a capacity, and maybe some try!s to the append calls, but within a function you can easily reason about that. I don't think that's too specialised a thing for folks to want to do.

Anyway, it's genuinely not my intention to argue against the LSG's decision here. I'm glad I asked and got your thoughts on it.

10 Likes
Skepticism about the imminence and eventual use of a fixed-capacity-but-variable-count Array-like type

I’ll note that since Swift doesn’t expose custom copy-constructors, it’s not possible to implement a Copyable fixed-capacity-but-variable-count non-heap-allocating type today; a non-Copyable version is certainly still useful, but no longer a drop-in replacement for Array. I’ve also seen it pointed out that even if it were one, it wouldn’t necessarily be a good idea to drop it in for non-trivial element types, because copies become eager instead of CoW-like. Someone should still make one! But I think it will be a more niche thing than the fixed-count one that’s just been accepted, whatever their names would be.

EDIT: John points out below that even the non-Copyable version needs a custom move constructor to work properly, since Swift has types that cannot be moved via memcpy. Whoops! So I guess such a type is even further off right now.

3 Likes

A non-copyable version is also not possible to write today; it would have to either also be non-movable or be restricted to elements that are bitwise-movable, neither of which is something we can express right now. (A bitwise-copyable constraint would be adequate but wildly over-restrictive.)

5 Likes

Well, happy to see this moving forward! I think I'll have a lots of uses for it, so I'm looking forward to be able to use it in my projects.

I was initially surprised by the name, as InlineArray was not discussed that much compared to other names in the pitch nor the review threads, mainly because (as @Karl pointed out above) the discussion was cut short knowing that another inline but dynamically sized array was going to be proposed.

After thinking about it for a while though, I see how it's better than FixedArray: even though the static-size is a more fundamental property in theory, the inline-ness of it has a much greater potential to be (catastrophically) overlooked by users of the type (while it's not possible to miss that the type doesn't have an append method when you attempt to use it).

What I imagined when this type was being pitched was that such a taxonomic naming system would be based on how those new types differed from types having a state of the art name, like Array (or Vector, had that been the chosen name). If a second shareable dynamic array type was needed in the future, the new type would only need to reflect the property that is different from that of the base array, (ie StrangeArray, if the new property is "strangeness"). Not repeat all the "base" characteristics of an array like its dynamicness or shareability.

In such a naming system it would have been particularly useful to have Vector, because that's a new state of the art term that could be used to generate names for the new types: it frees InlineArray for a dynamic-but-inline array type, and if a new "dimension" needs to be added in the future we could have had StrangeArray+StrangeVector instead of StrangeArray+StrangeInlineArray (or was it InlineStrangeArray?).

Maybe that sounds far fetched, but if having ten or more array types across the standard library and other core libraries is a possibility, I can't help but think that a new state of the art term may have lightened the naming load.

I think it's interesting that a small variation of existing types, like a dynamic-but-inline version of Array (which is a useful thing to have, regardless of whether it's possible to write such a type today) already runs into naming issues: it could be DynamicInlineArray, but then it's odd to need to spell out "dynamic" because the base Array already is dynamic (even though InlineArray isn't).


Anyhow, I don't want to come across as too negative: I'm super excited that this type is coming, I've needed something like this in the past and I know I'll use this a lot in the future. Naming is important but ultimately we'll get used to whatever the choice is as long as the name is not too long and in that regard InlineArray is a perfectly usable name.

5 Likes

hey i'm not all that experienced, a bit of a semi beginner - but the name inlinearray makes me think it's an array that's inlined, i think it's a confusing name if its indeed not inlined and that the inlined type is for a future proposal. can this be reconsidered?

additionally, a beginner would ask "whats inline?" while "fixed" is quite easy to explain if not even they'd infer the meaning from the context themselves because it just makes sense.

but why not embrace vector? sure it'll make it a bit confusing for devs experienced in other langs who are used to the opposite, but tmk swifts naming would be more accurate than theirs no? wouldn't those languages be to blame in that case :thinking:

For all intents and purposes, this type would be inlined. Take this struct, for instance:

struct Metric {
    var id: UUID
    var topTenValues: InlineArray<10, Int>
    var average: Int
}

If this Metric type shows up as a local variable, topTenValues would be laid out inline on the stack (along with id and average). If Metric were itself in a regular Array<Metric>, the three properties would appear inline in the array storage, rather than indirectly pointing to yet another array, which would live somewhere completely different on the heap.

4 Likes

Appreciating that it's finally accepted. I want to use this in particular because of the C interop changes. Any idea when this might land in an official toolchain (as per Xcode) release?

As a result of the first review, the changes to C interop were removed from this proposal pending further design work.

5 Likes

Oh, bummer. Must have missed this. It was the feature I was longing for… ok, then it's Tuples for some more years.

6 Likes

I have no words to express how disappointed I am that I can't finally get rid of tuples for C interop.

10 Likes

I would definitely recommend reading the proposal's section on why C interop has been deferred, as this proposal itself does not provide the necessary migration path or integrations of that interop. imo, we shouldn't be too surprised that this is discussed on the forums soon.

2 Likes

Yeah, this is confusing.

I hope the standard procedure after the acceptance of a proposal can be formalized further. For example, whether the proposal text should be updated to match the announcement within some period.

I'm bummed about the lack of change to the C interop, but understand. (C'mon Swift 7....?)

I have (tuple -> array)/(array -> tuple) converters that are VERY unsafe and only work for simple types that I use for C interop (assumingMemoryBound, pointers, fast version has a memcpy, etc) (Most recent public version, I think)

Will it be worth it to rewrite them to use InlineArray? Will I be able to point at more complicated types?

Would this or Span be the better storage type for a place-holder personal use FixedSizeCollection type if that types main point is interacting with C? (example above uses Data, which wouldn't be an option in Foundation-less contexts)

Looking forward to having this type available!