Is Swift becoming unergonomic Rust?
(No, but I thought it was)

Going to preface this post with this: I'm not a programming language designer. I don't really know what I'm talking about. I'm just a Rust developer with a passing interest in Swift. I probably made some mistakes in here, feel free to send me corrections.
So about a week ago, I was bored and watching WWDC 25 (pronounced "dub dub
DC", apparently.) I was watching this video:
Improve memory usage and performance with Swift, where they show off some new features in Swift to reduce memory overhead
and increase safety (feel free to watch this alongside!) The first new
standard library construct they show off is
InlineArray<N, T>
, which acts like... an array. In Rust
terms, the normal Swift array is similar to a
Arc<Vec<T>>
, where it is a growable collection that
can be shared across threads and is copied on write. To keep the analogy
going, the new Swift InlineArray
is like a Rust array:
[T; N]
. To make it clear, Array
is heap-allocated
and InlineArray
is stack-allocated.
And let's compare to Rust:
fn vec() {
// I could wrap this in an Arc, but
// that's not really what real code would do.
let mut vec = vec![4, 5, 6];
}
fn array() {
let mut arr = [4, 5, 6];
}
You can probably see the argument I'm going to make here — This is less ergonomic. Rust is often said to have bad syntax, but here you can see that the better performing option is just easier than the heap-allocated version.
Safety
So another topic in the video is how to efficiently read from a reference counted collection. In order to avoid the reference counter, and read data directly, one would previously use an unsafe pointer to do a direct read.
// Safe usage of a buffer pointer
func processUsingBuffer(_ array: [Int]) -> Int {
array.withUnsafeBufferPointer { buffer in
var result = 0
for i in 0..<buffer.count {
result += calculate(using: buffer, at: i)
}
return result
}
}
As highlighted in the talk, these buffers are unsafe partially because they
could be used outside of the scope of the function (e.g. returned, or stored
as part of a structure), and misued. In order to make this operation safe,
they introduce a type called Span
, which points to a
contiguious block of memory. In order to make this safe, a new "Marker
trait" (Rust speak) called Escapable
is added to the language. This trait is auto-added to all types by default,
and you can specify if your type cannot be used outside of its context with
~Escapable
. Pretty neat language feature!
This is a very different approach to Rust. We deal with pointer unsafety by
relegating them to Unsafe Rust, a language that lives alongside
Safe Rust. But also — We sidestep this whole problem.
Vec
s are not reference-counted (you have to wrap them in a
Rc
or Arc
for that,) so we don't have to sidestep
the reference counter.
Conclusion
You can clearly see the difference in these language's designs from these
two examples. Swift makes working with types as easy as possible, while
letting you "drop down" for performance. Rust instead makes the most
ergonomic pattern the most performant, while letting you "build up"
convenience by moving to heap allocation with Vec<T>
,
adding copy-on-write with Cow<T>
, or reference counting
with Rc<T>
.
I think it's clear who these language features are for — Swift library maintainers. Being able to write more performant code for your users will make everyone happy, and doing this without unsafety is nice. I don't think this is really for app developers, or anything like that. Making all of your array accesses use spans to dodge the reference counter is technically faster, but makes things much more annoying to maintain than the obvious solution. I think the talk makes this clear, as they show off a new Binary Parsing library at the end which uses these new language features.
As for the title of this article. I don't think Swift is becoming unergonomic Rust (Betteridge's law of headlines wins again.) It's just bringing in features similar to Rust to "drop down" to. Honestly, a "less complex" Rust has a good place in the world of programming languages. There is some work adjacent to this, see the Alloy language: a fork of Rust that gives you opt-in garbage collection for things like Linked Lists. I think it's easy to write things like this off — but all languages have their tradeoffs. What makes you a good developer is being able to take all of the values of prospective systems and being able to pick the right one!