(Originally published on my old blog)
I’ve been learning Rust recently.
Rust is one of the most loved languages out there, fast and has an amazing community. Rust invented the concept of ownership as a solution to memory management problems without resorting to slow processes like garbage collection or reference counting. But, when you don’t need to be very low level, it gives you utilities like Rc, Arc And Cow To do reference counting and “clone-on-write” in your code. And, when you still need to go to a lower level, you can use unsafe Access the system and raw C pointers.
Rust also has amazing features of functional languages like tagged enums, match expressions, first class functions, and a powerful type system with generics.
Rust has an LLVM-based compiler that lets it compile to native code and WASM.
I’ve also been doing some Swift programming for the past few years. And the more I learn Rust, the more I see a reflection of Swift. (I know Swift stole a lot of ideas from Rust, I’m talking about my approach here).
Swift also has amazing features of functional languages like tagged enums, match expressions, and first class functions. It also has a very powerful type system with generics.
Swift also gives you full type safety without a garbage collector. By default, everything is a value type with “copy-on-write” semantics. But when you need additional speed you can choose to “move” the proprietary system and values to avoid duplication. And if you need to go even lower, you can use unsafe systems and access raw C pointers.
Swift has an LLVM-based compiler that lets it compile to native code and WASM.
#Deja vu?
You probably feel as if you have read the same paragraph twice. This is not an accident. Swift is similar to Rust and has most of the same feature-set. but there is a huge difference Perspective. If you consider the default memory model, this will start to make a lot of sense.
#Rust is bottom up, Swift is top down.
Rust is fundamentally a low-level systems language, but it gives you the tools to go to higher levels. Swift starts at a high level and gives you the ability to go to a lower level.
The most obvious example of this is the memory management model. Swift uses value-types by default copy-on-write Semantics. This is equivalent to using Cow<> For all your values in Rust. But mistakes matter. Rust makes it easy to use “transfer” and “borrow” values but requires an additional function to use. Cow<> values, as you need to “open” them .as_mutable() To really utilize the value within. Swift makes it easier to use these copy-on-write values instead of requiring an additional function to borrow and move. Rust is fast by default, Swift is simple and easy by default.
#Swift takes the ideas of Rust and hides them in C-like syntax.
Swift’s syntax is a masterclass in taking amazing functional language concepts and hiding them in C-like syntax to trick developers into accepting them.
Consider match statement. The match statement in Rust looks like this:
The same code in Swift would be written like this:
Swift doesn’t have match Statement or expression. this is a switch Statements that developers are already familiar with. Except this switch The statement is not actually a switch Quite a statement. This is an expression. It doesn’t “collapse”. This pattern matches. it’s just one match Expressions with different names and syntax.
In fact, Swift behaves enums as more than Now! type and lets you put methods directly on it:
#alternative type
there is no rust nullbut it is None. Swift has nilbut it’s really just a None In hiding. instead of one OptionSwift let’s you use T?But the compiler still forces you to check that the value is not nil Before you can use it.
You get the same protection with more convenience because you can do it with optional types in Swift:
Also, you won’t be forced to wrap each value with a Some(val) Before returning it. The Swift compiler takes care of that for you. A T will be converted transparently T? when needed.
#error handling
there is no rust try-catch. instead it has a Result Types which include success and error types.
Swift doesn’t have try-catch either, but it is do-catch and you have to use try Before calling a function that may throw. Again, this is just a hoax for developers coming from C-like languages. Swift’s error handling works exactly like Rust’s behind the scenes, but it’s hidden in a clever, familiar syntax.
This is very similar to what Rust lets you use ? at the end of statements to automatically forward errors, but you don’t need to wrap your success values in Ok().
#Rust’s compiler catches problems. Swift’s compiler solves some of them
There are many common problems that Rust’s compiler will catch at compile time and suggest solutions for you. The example that illustrates this well is the self-referential enum.
Consider an enum that represents a tree. Since this is a recursive type, Rust will force you to use something like this Box<> To refer to a type within oneself.
(You can also join us Box instead)
This clarifies the problem and forces you to deal with it head-on. Swift is a little higher, automatic.
Comment:that you still have to annotate it enum with indirect Keyword to indicate that it is recursive. But once you do that, Swift’s compiler takes care of the rest. you don’t need to think Box<> Or Rc<>. The values work normally.
#Swift is less “pure”
Swift was designed to replace Objective-C and needed to be able to interface with existing code. Therefore, it has adopted a lot of practical choices that make it a much less “pure” and “minimal” language. Swift is a much larger language than Rust and has many more features built-in. However, Swift is designed with “progressive disclosure” in mind, meaning that just as you think you’ve learned the language, a little more of the iceberg comes out of the water.
there are buses here Some? Language Features:
- Classes/Inheritance
- async-await
- async-sequence
- actors
- recipients and settlers
- lazy trait
- property wrapper
- Result builders (for building tree-like structures. e.g. HTML/SwiftUI)
#Convenience has its cost
Swift is a much easier language to get started with and be productive with. The syntax is more familiar and much is done automatically for you. But this is what actually makes Swift a higher-level language and it comes with the same tradeoffs.
By default, Rust programs are much faster than Swift programs. This is because Rust is fast by default, and facilitates You stay slow, while Swift is easy by default facilitates You hurry up.
Based on this, I would say that both the languages have their uses. Rust is better for systems and embedded programming. It is better for writing compilers and browser engines (servoes) and it is better for writing entire operating systems.
Swift is better for writing UI and servers and compilers and parts of the operating system. Over time I expect to see the overlap grow larger.
#“Cross-platform” problem
There is a perception that Swift is a good language only for Apple platforms. Although this was once true, this is no longer the case and Swift is rapidly becoming a good cross-platform language. Hell, Swift even compiles for wasm, and the fork created by the swift-wasm team was merged back into the Swift core earlier this year.
Swift on Windows is being used by the browser company to share code and bring the Arch browser to Windows. Swift on Linux has long been supported by Apple itself to pursue “Swift on the server”. Apple is directly sponsoring the Swift on Server conference.
Embedded Swift was also announced this year which is already being used on small devices like the Panic Playdate.
The Swift website is highlighting several of these projects:
The browser company says that interoperability is Swift’s super power.
And the Swift Project is trying to make working with Swift outside of Xcode a great experience with projects like open source LSP and funding VSCode extensions.
#Swift is not a perfect language.
Compile time (like Rust) is quite bad. There is a certain amount of feature creep and the language is much larger than it should be. Not all the syntax looks familiar. The package ecosystem is not as rich as Rust.
But “Swift is only for Apple platforms” is an old and cliché at this point. Swift is already a cross-platform, ABI-stable language with no GC, automatic reference counting, and the option to choose ownership for even more performance. Swift packages are working on Linux faster. Foundation was ported to Swift, open sourced, and made open source. It’s still early days for Swift as a good, more convenient, Rust alternative for cross-platform development, but it’s here now. There is no future worth waiting for.
<a href