Swift Static Linux SDK
swift.org
swift.org
Along with the move to a non-Apple GitHub organisation, it represents real progress in extending swift to other platforms.
It would be interesting to see if this is used for the AI OS that they are inviting researchers to validate for security purposes.
That would imply either:
1. Bare-metal Linux AI OS runs on Apple Silicon. Apple could contribute Apple Silicon firmware/hardware support to Asahi and mainline Linux.
2. Virtualized Linux AI OS runs on Apple Silicon Hypervisor. Apple could contribute mainline Linux guest support for Apple Hypervisor. They could also support Linux VMs on MacOS and iPadOS which run on Apple Hypervisor, for M2+ devices with hardware support for nested virtualization.
I'm really glad there's finally support for vanilla Debian and from what I've seen it looks like there are initial Swift packages finally being added in the Debian testing version, which will be nice. Debian is more and more my go-to for development VMs where Ubuntu used to be, so I'm excited for official support!
With all this and embedded, I'm really excited about Swift! I've done a lot of embedded in C and I'm super keen to try out Swift on my STM dev boards!
Not sure I understand that. Is it something specific to Swift, or is it exactly what is expected from using shared libraries?
Say my Linux distribution distributes some Swift runtime, then the corresponding Swift packages should have been built for this runtime as well. Just like when my Linux distribution distributes a libc, the corresponding packages need to be built for this libc. Right?
Still, it's cool that Swift provides static linking. But like in any language, the best IMHO is when the Linux distribution can choose how it wants to distribute a package. I tend to like shared libraries, and already Rust seems to be interfering and imposing its preferences. I am happy if Swift doesn't.
My opinion is the opposite: I think the old paradigm of distros managing a giant set of system libraries is a bad one, and is how we ended up in the land of docker. Go and Rust made the right decisions here: vendor all the dependencies, and distros can't mess with them. Makes it easier for upstream and the distro.
Modern languages are not like C/C++: a single non-trivial rust program can easily depend on 100+ crates, and re-creating crates.io in your package manager is just a bad idea, even putting aside that there's probably major version incompatibilities the moment you go beyond a handful of programs. Look at the disaster that's python package management, that's not where you want to end up.
IIRC Arch does as well (or at least tries to).
Until there's a vulnerability in one of the dependencies and now you have to rebuild all of the packages which use it. Specifically for Rust, there's also the fact that most projects use a lock file and if your build process respects it, you now have to wait for the upstream to update it and release a new version (or update it yourself). And if your build process doesn't respect the lock file (or the project doesn't use it) and you just fetch the latest compatible dependencies at the time of build, you now have no idea if you're affected or not by the vulnerability because you don't have the exact resolved versions stored anywhere (https://github.com/rust-lang/rfcs/pull/2801).
Static linking comes with a lot of issues, just like dynamic linking is not perfect. I really think it depends on the use-case, and that's why I want to have the choice.
Packages get rebuilt all the time. This is fine.
As for the rest, it would be cool if binaries shipped with a manifest of some sort naming all the versions of their statically included dependencies. A SBoM of sorts. It would make this sort of vulnerability scanning much easier to do.
Scanning, sure. Fixing... surely much harder than with shared libraries.
It should be pretty easy to programmatically update a lock file, run the tests, and rebuild a package. For rust crates that are compiled and packaged straight from git, you could probably automate that today.
You still fundamentally need to rebuild (or at least relink) and re-distribute all the packages, whereas with shared libraries... well you just update that one package.
You also now get a huge set of users who will now download these 1k of updated packages. Not to mention the utter fun of trying to understand why your system just requires maybe 100 packages to be updated…
Compare to upgrading a single shared library.
At large timescales, yes. But not at timescales relevant for rapid security updates.
They get rebuilt when a change in the package is made, not when a change in a dependency is made — which is a huge difference.
There are plenty of packages that might not get an update for a long time (month, half year, … whatever their release cadence might be).
Dynamic linking makes it _easier_ to handle security, including checking if your program is linking to a broken library — static linking does not have the same means.
Statically linked programs don’t mean you know what is being run either, you can always dlopen() and all kind of shenanigans like Go and Rust do.
What is a security nightmare is statically linked binaries, you have no clue what they are linked against.
Debian did the strongest push for reproducible builds, and is 100% source available.
Enclaves like Go, Rust, Swift (things that insist on using direct sys calls, and eschewing system libraries) would need to implement their own versions, or use FFI, and then you're SOL anyway if you really insist to use static linking... And who knows what kind of bugs might crop up there that don't match whatever anything anyone else uses.
Shrug :-)
On the other side, there are quite a few self-updating applications out there (browsers), and source control systems like Github will push notifications, and even pull requests for out of date dependencies. So pushing out a new version of a given application is pretty easy. If the application comes through Flathub or self-updates then it doesn't have to wait for the distro maintainers to get the update out.
The amount of work spent finding usages of static linkage, and figuring out which version of libxz (or whatever it was called) just to be very sure that one wasn't using the compromised version was atrocious, no thanks.
No, but it has to wait for each developer of each binary individually. Instead of relying on a team of maintainer, you now rely on all the developers.
"Self-update" isn't something magical: someone has to do build the new version and ship it. With static linking you still have to build and ship for every. single. binary. With dynamic linking you just build and ship the one library that everybody depends on.
I tend to keep my host OS pretty bare, and most of my runtime apps in Flatpak and the stuff I'm working on in Docker.
Do you have experience with that, or do you say it because you believe it's possible? Genuinely interested, because I have been looking into it and my conclusion was that it does not really do what I expect from "dynamic linking".
See e.g. https://news.ycombinator.com/item?id=40665343 and https://news.ycombinator.com/item?id=40661874
AFAIK, the reason most Linux distributions have an allergic reaction to static linking is because of zlib. There was a vulnerability in zlib some time ago, and it required the distributions to find every single program which had its own copy of zlib (vendoring zlib used to be common back then), update these copies with the security fix (not necessarily trivial, since the vendored copy might be a customized variant of an older zlib release), and rebuild all these packages (which is a lot of "fun" when some of the packages are in a "failed to build from source" aka FTBFS state). The distributions learned their lesson back then.
For randomly downloaded binaries from the internet it means you will most likely keep using it with the vulnerabilities for years to come.
Yes, for you as a user it's simpler to link statically and not learn about anything else. But distros are a bit more elaborate than that.
I’m happy for someone to update the version of my dependency if it has security patches. But the rest? A total nightmare. I’ll take cargo / npm / swift / etc style versioning every day.
If you make open source software, the solution is simply that you should not distribute your software [1]. Let distro maintainers do it, and suddenly it's a lot easier for you.
If you make proprietary software, then it's not exactly the problem of the Linux distros anymore so sure, feel free to link statically. I personally think you should do a mix of both: libraries like openssl you want to link dynamically from the system. Now some obscure dependency you may have that breaks compatibility randomly and is generally not distributed by major distros, probably you want to link them statically. But you should know that you are then responsible for them and the security issues they may bring (i.e. you should monitor them and update them regularly).
[1]: https://drewdevault.com/2019/12/09/Developers-shouldnt-distr...
You forgot to link the image with the dog sitting in the fire.
Go binaries have that. You can apply "go version -m" to any recent Go binary.
Also "govulncheck".
Examples:
go version -m $(which go)
go install golang.org/x/vuln/cmd/govulncheck@latest
go version -m $(which govulncheck)
govulncheck -mode=binary $(which govulncheck)
for b in $(go env GOPATH)/bin/*; do govulncheck -mode=binary "$b"; doneThe whole point of a distro is that someone distributes them, so you can choose which distro you want to trust.
What I don't like about Rust and Go is that they enforce their preference. I am fine if you want to link everything statically. I just don't want you to force me.
Everything about the shared lib model is stupid, and has contributed to poor Linux market share to date.
> Everything about the shared lib model is stupid
I don't think we can discuss if that's you're stance. But also I don't think you understand the shared lib model if you think like that.
Rust supports dynamic linking, and has since well before 1.0.
> Yes, it's more painful for end users than just typing `cargo build`
Cargo is fine, I don't mind installing cargo. I just don't want my packages to be handled by my package manager. I don't want every project to ship their own custom package manager like rustup.
I don't understand why one would think this. You make it sound as though Rust only supports dynamic linking for glibc, but that's certainly not true.
I've seen plenty of bad go libraries. For example their authors will be unaware of isatty() so they will output control codes when piped.
If you factor in the time to find a competently written library (and quite possibly write one yourself) it starts to be less convenient.
The one where the only sane option is using distribution packages or conda and ignoring anything that the python community comes up with?
Ok installed Meson also from my distro packages! But no, the project required a newer Meson version.
Ok let's install Meson with pip! But no, it turns out pip packages themselves can require a minimum pip version!! Go figure.
So I couldn't build that program without first pip-installing Meson, and I couldn't pip-install Meson without first pip-installing a more modern version of pip itself.
Guess how well it worked when I upgraded pip. Spoiler: not a smooth way to discover that Python packaging is a joke.
This is exactly what Node/NPM does: "npm instal" creates a new dir, "./node_modules" and installs stuff there, and next "npm" commands will transparently refer to that subdir. This is, in effect, the same thing as a Python's venv! to the point that in my scripts I always name them "python_modules", to give other unfamiliar devs a clue of what it is.
If "pip install" just did this transparently, without introducing the whole concept of venvs to the user, it would be a huge step forward. I cannot imagine how it would suck if NPM didn't make its node_module dir, and instead it forced you to first learn all the idiosyncrasies of module isolation.
What does that mean?
I think there's a bit of a misconception here.
Ardour is written in C++ and depends on 80+ other libraries.
The dependency situation may be exacerbated by packaging culture that encourages the use of lots of relatively small dependencies, but it is by no means determined by language or even context.
When in C++ I had 2 direct dependencies it resulted in 6 total dependencies (direct + transitive).
In Rust I had 5 direct dependencies (because some standard stuff is not in the standard lib) and it resulted in... 300 total dependencies.
> but it is by no means determined by language or even context.
Not sure what you mean there. My feeling is that making it super easy to pull 50 transitive dependencies without realizing it does not help. If you have to manually handle every single dependency, first it forces you to look at them (that's a good thing), and second it encourages you to minimize them.
What is it that makes you think that C/C++ does not do this also?
[ EDIT: this also has something to do with the typical size and scope of C/C++ libraries ]
My experience. I haven't worked in a single C++ project that had hundreds of dependencies, but I encounter those regularly in Rust/npm.
More of a shared library issue I believe.
> Say my Linux distribution distributes some Swift runtime, then the corresponding Swift packages should have been built for this runtime as well. Just like when my Linux distribution distributes a libc, the corresponding packages need to be built for this libc. Right?
That's correct
> Say my Linux distribution distributes some Swift runtime
That runtime would need to be compatible with the Swift program. Nowadays that’s not a big issue due to ABI stability (https://www.swift.org/blog/abi-stability-and-apple/), but this would close the door on new features that need runtime support (need an OS update then, or the OS must come with multiple runtimes).
My beef with the "static linking trend" is that many people (or languages, e.g. Rust) don't want to let me link dynamically, for some reason. Just let me choose!
Debian's build of rust packages are linked dynanically, for instance. It's a build setting, you can turn it on.
That's not at all how debian packages written in rust are linked. So the person I replied to is incorrect.
I suspect you're incorrect as well and what you claim doesn't exist at all, but you're claiming a different thing.
Yes, by passing crate-type=dylib to the compiler.
Those dynamic libraries will also not remain ABI stable if their code changes (because Rust's semver specifically applies to the public API, but the public API is free to pass around structs with private fields, which may alter the layout drastically even for minor semver-compatible changes).
So this helps with some aspects (such as libraries being reused between binaries, reducing disk usage).
But it does not help with the issue most discussed in this thread: that of security updates (all libraries have to be rebuilt whenever rustc changes or a dependency changes, all programs (potentially the entire operating system) have to be redownloaded by all users, etc).
Not listed is that ASLR either doesn't work at all or you get just one object (the PIE's) randomized. For a memory safe language this may not be a con at all.
Also, when many executables can share common objects then there can be a reduction in I/O (reads) needed to load those the next time that one of those executables runs. In 2004 when the Solaris 10 (then a work in progress) unified process model delivered, and all system executables were dynamically linked, boot times went down by a lot.
--- stderr
configure: error: in `/home/andrew/rust/ripgrep/target/x86_64-unknown-linux-musl/release/build/jemalloc-sys-b7d053053989ba56/out/build':
configure: error: C compiler cannot create executables
See `config.log' for more details
thread 'main' panicked at /home/andrew/.cargo/registry/src/index.crates.io-6f17d22bba15001f/jemalloc-sys-0.5.4+5.3.0-patched/build.rs:351:9:
command did not execute successfully: cd "/home/andrew/rust/ripgrep/target/x86_64-unknown-linux-musl/release/build/jemalloc-sys-b7d053053989ba56/out/build" && CC="musl-gcc" CFLAGS="-O3 -ffunction-sections -fdata-sections -fPIC -gdwarf-4 -fno-omit-frame-pointer -m64 -static -Wall" CPPFLAGS="-O3 -ffunction-sections -fdata-sections -fPIC -gdwarf-4 -fno-omit-frame-pointer -m64 -static -Wall" LDFLAGS="-O3 -ffunction-sections -fdata-sections -fPIC -gdwarf-4 -fno-omit-frame-pointer -m64 -static -Wall" "sh" "/home/andrew/rust/ripgrep/target/x86_64-unknown-linux-musl/release/build/jemalloc-sys-b7d053053989ba56/out/build/configure" "--disable-cxx" "--enable-doc=no" "--enable-shared=no" "--with-jemalloc-prefix=_rjem_" "--with-private-namespace=_rjem_" "--host=x86_64-unknown-linux-musl" "--build=x86_64-unknown-linux-gnu" "--prefix=/home/andrew/rust/ripgrep/target/x86_64-unknown-linux-musl/release/build/jemalloc-sys-b7d053053989ba56/out"
expected success, got: exit status: 77
Notice, in particular, that there is `CC=musl-gcc` in the error above. But: $ which musl-gcc
musl-gcc not found
I think the spoiler here is the fact that ripgrep uses jemalloc when you build with musl on 64-bit: https://github.com/BurntSushi/ripgrep/blob/c9ebcbd8abe48c833...If you comment out those lines and remove the `jemallocator` dependency from `Cargo.toml`, then the build succeeds.
I imagine you'll run into this same problem if you enable ripgrep's `pcre2` feature.
However, after installing the `musl` Archlinux package (which provides `musl-gcc`, among other things of course), then the above command builds just fine. Including with `jemallocator` or even with the `pcre2` feature enabled.
"Trend"? Maybe "recent reverse trend" is more appropriate.
Static linking preceded dynamic linking by several years, for compiled languages, AFAIK.
After exploring most of these for my use case (something that needs to run on an "embedded" RPi CM4 as well as iOS), I wrote a Swift bridge/runner for Flutter [7] which is working well. That enables me to write the UI in Dart and the business logic in Swift. I looked at all the other options below (that's how I know about them, indeed [3] I wrote as part of my research), in the end I felt that none of them were at the time of investigating sufficiently mature to base a product around.
[1] https://github.com/rhx/SwiftGtk
[2] https://github.com/Longhanks/qlift
[3] https://github.com/PADL/LVGLSwift
[4] https://github.com/TokamakUI/Tokamak
[5] https://github.com/stackotter/swift-cross-ui
It's confusing because the Swift language also has the concept of "protocols" which are basically the equivalent of Java and C#'s interfaces if you're familiar with those languages.
Think of it this way: in 5-10 years, ObjC will be the python2 of Apple development.
Lua is nice. But it’s chalk and cheese comparing the languages. I can’t think of many cases where you’re trying to decide between lua and Swift.
Sure, Swift has sexy features that make it appealing to developers. But the ecosystem for server infrastructure on Lua is formidable.
It's not perfect, but I hope it continues to flourish because it gets a lot right
SwiftUI, like react uses some language constructs to make it look really fancy but in reality it's really simple nested data structs.
The real hard part is what the "DOM" provides for React and likewise what UIKit/iOS is doing with those data structs.
https://github.com/stackotter/swift-cross-ui (Linux + Windows)
https://github.com/TokamakUI/Tokamak (WASM + MacOS + some Ubuntu support)
They both had to rewrite the entire backend from scratch, because SwiftUI is very, very closed source. But Tokamak does have an import that just forwards to SwiftUI on Apple platforms.
So yes for me, I might switch to Swift which seems very elegant and readable compared to some other options.
My coworker wrote down some of the major issues we were facing: https://forums.swift.org/t/issues-learned-4-years-with-serve...
Personally, I've been soured enough that I don't ever want to write any server-side code in Swift again. It just seemed that Swift pretended to be open-source and cross-platform and then Apple would just unilaterally push some changes just because they wanted them for iOS (e.g. function builders for SwiftUI) without waiting for community feedback. It's fine to be a dictator for your PL, but not when you're claiming to have "open governance". All the while support for Linux was routinely being neglected.
I would rather recommend Kotlin. It's similar enough to Swift and it comes with the advantage of having the whole JVM ecosystem which can probably do most things you'd ever want to do on a server.
https://forums.swift.org/t/text-streaming-in-standard-librar...
While these things are not difficult to solve with some minor additions to the standard library and documentation that emphasizes some basic conventions, the fact that we're on Swift6 and things like this aren't just available speaks to how much further the community needs to evolve to get the language ergonomics right for non apple-y things.
for try await line in file.lines
?but the language got complex and is now a baby C++.
And what’s the complexity now that even remotely approaches c++?
If you give a Swift file a shebang, it’ll execute as a standalone script.
I still don’t agree they’re necessarily as overlapped as the other person claims but there is a decent area where Swift is easy enough to replace Python tooling
Python is mostly used not because of its language features but for cross-platform ecosystem. This is hard and slow to build especially these days (comparing to 10 years ago) since there are many languages and winner takes all or most of the cake.
The only way is
1) to provide something really groundbreaking at the time (e.g. ruby on rails)
2) piggyback on other ecosystem by providing great interop (e.g. maybe mojo in the future)
3) slowly grind and hustle until something eventually catchup (e.g. maybe rust in the future)
The problem with apple is they are too much focused to wall garden you instead of build ecosystem. Their IBM partnership failed to make something like Rails or Spring Framework for backend. Tensorflow for Swift failed even though it was google project. They don't have cross platform support in their DNA. You don't even get Xcode on Windows or iPad - this is dealbreaker for many people.
I would bet more on kotlin (native) / java even though I'm iOS dev myself. Embedded is the exception that they still might win the game since the only competition there is pretty much: C, C++, Rust and sometimes python, js more as a glue.
For the average developer, writing modern Swift is generally not any more difficult than it was several years ago (much more often it's easier), it's just that the language can do a lot more now. But you only need to learn those new parts if you're using them. If you're not writing low-level or special-purpose code, you don't need to learn about more recently added things like move-only types, the embedded subset, varadic generics, or macros, etc, etc. And if you are writing things that require those features, you often couldn't have even considered Swift for that purpose before they were added.
I think it is a lot more difficult now, because the scenario wrt the, uh FUCK, WHAT THE— CEASE ALL MOTOR FUNCT—
- An "embedded" mode that turns off reflection for kilobyte-sized binaries
- An upcoming WASM target
- An LSP for VSCode
- Native C++ interop
- Typed "throws"
- Static linking on linux and the linux sdk
- Porting a number of "core foundation" / foundation primitives to nix platforms
- Distributed actors for concurrency that run on distant machines
- Data-race free by default
- non-copy RAII generic types
Watch the video on Swift 6: https://developer.apple.com/videos/play/wwdc2024/10136/.
IMO Swift 6 is a better Go and than Go, and a better Rust for app-dev type stuff, which seems to be what people want to use Rust for.
Swift has shipped stuff that Rust has been sitting on its hands over for years:
- Anonymous enums/structs in parameters/match statements
- Named function arguments
- A type of `Send` bound that allows Rcs to be sent between threads (not Arcs)
- Swift preview hotreloading
- autoclones (without GC)
Swift also took a bunch of goodies from Rust *[1]
- No garbage collector
- Traits via "protocol"
- ADT for Option/Result
- macros
- non-copy types enforcing RAII
I really really wish Rust had been keeping up with Swift's ergonomics because it's looking like Swift is going to catch up to Rust's cross-platform story much faster. Swift really could end up as the "do it all" language for frontend/backend/app dev.
[1] By "took" I mean if you like these things in Rust you'll see them in Swift
Go hyped their compilation speed a lot in its early years, but unless it's C++ templates level slugginess it's not like it's that big of deal.
I really like Swift, but I’d highly appreciate faster compilation times.
In Swift 6 they:
- Improved parallelism via compilation pipelining (ie top-level object files are referencing not-yet-compiled symbols but can still proceed while those compile in the background)
- Parallelized and pipelined debug symbol generation (lazy debug symbols)
It’s a much more powerful language. In my opinion slower compilation is worth it for all of its great features. (Does Go even have parametric enums?). But its definitely a trade off.
I know they’re working on improving it. I’m glad they are. But that speed is something I definitely miss.
I'd say they took them from Apple's own Objective-C additions who had those for decades before Rust (ARC and protocols).
Edit: back from watching the video, and a few observations:
- Swift does not suffer from Rust's refusal to add things into the standard library. gasp regex support in stdlib? Async/await runtimes?
- Default testing framework is a godsend, especially when integrated into the build system
- Rust but better for everything that's not low-level systems development is the impression I get
- "Introducing a Memory-Safe Successor Language in Large C++ Code Bases" (John McCall; CppNow 2023) https://www.youtube.com/watch?v=lgivCGdmFrw
- "Swift as C++ Successor in FoundationDB" (Konrad Malawski; Strange Loop 2023) https://www.youtube.com/watch?v=ZQc9-seU-5k
And then a series of blog posts by Doug Gregor:
- "Swift for C++ Practitioners" https://www.douggregor.net/posts/
And finally:
- "Mixing Swift and C++" (Swift.org) https://www.swift.org/documentation/cxx-interop/
I think they want to be able to use a limited subset of it in their kernel. Actually I think they already are but I’m not positive.
Obviously it started as an application level language but I do think they’re trying to move it down the stack as well.
I find myself reaching for Swift as the default for a lot of things rather than Rust, especially with the advent of C++ Interoperability and the new Swift embedded stack and ownership model.
The only thing I really need is a UI story on other platforms, but it’s an area Rust is lacking great solutions too. I suspect the C++ interop might eventually make Swift have great Qt bindings to compete with PySide as an accessible way to make multi platform UIs.
https://github.com/qt/qtdeclarative/tree/dev/tests/manual/he...
I’ve kicked around with the C++ interop a few times. Projects like Qt that use raw pointers extensively seem hard to expose. Ended up having to wrap Qt classes to model as a value type which was straight forward but it’s not nearly as seamless as the Objective-C++ experience
1) fast
2) expressive
3) concurrent
4) compiles natively
then I think Swift is the better of the two.
If you just want a very simple language, then sure, Go is there. But I don't think that's necessarily what makes Go, Go.
It’s nice to write but a pain to review. Meanwhile if I read Rust or Go, it’s more or less what you see is what you get.
I do agree in general that it’s frustrating that Rust hasn’t kept up with some of Swift’s pushes, but then again that’s the difference when you have the richest company in the world involved.
For context, I’ve written many apps in Swift, deployed a backend in Vapor, and worked with teams who use the language. I always thought I’d get over it but it just never fully clicked.
Where are channels and select? Why is async code special? Why do protocols have to be explicitly adopted? Why are errors "thrown" instead of returned?
Normal languages just sort of call functions on threads - and use locks when there is contention. You certainly can write the same sort of code in Swift - but that is not how async functions work.
The issue with locks is that waiting on a lock could be expensive - depending on the type of lock. And it also ties up the thread. If you have a thread pool you could end up with a lot of threads getting tied up in locks.
Async functions are suspendible. When they encounter a data hazard - the thread is released and allowed to go work on something else. When the conflict clears the function is picked back up and execution continues. Note: the way this is designed means that a _different_ thread may continue execution of the async function, and async functions should assume that they may change threads during execution. But this does prevent stalled threads just waiting on some condition. The compiler needs to extra notation to know your function has been designed around this sort of operation.
Basically: Async functions are not normal functions - they're allowed to be suspendible and may change threads mid execution - hence the extra notation. This improves both performance and safety.
I’m am not well educated on the subject though so take that with a grain of salt.
For those curious I looked at Windows and Linux, but not much else.
Linux: no io_uring support. There's debate on even whether to use it as people are discussing security implications[2]. It looks like (from perusing this issue, but could be wrong) AIO wasn't used.
Windows: it looks like they're using IOCP everywhere. Seems sensible enough.
General case: there seems to be an open issue regarding this[3].
[1]: For example, Windows has IOCPs, Linux has io_uring, FreeBSD has kqueue, POSIX has... POSIX AIO, etc.
I was not aware that file I/O isn't async on Linux. Even so, I'm pretty sure network I/O and channel operations (send/receive/select) are async via epoll. I'm not as sure about these, but I think time.Sleep and sync.Mutex.Lock suspend the goroutine as well.
The obvious first candidate was Rust, but the low-level features were unnecessary overhead. A "higher-level Rust" is literally Swift, but the portability is not there. The search basically ended here, so I went with Go as a consolation prize - I don't get a fancy type system to play with, but the development ergonomics, especially with the v2 modules system, are the best I've experienced yet.
I think the main problem, and this is a really big problem, is that based on a very shallow investigation I did a couple months back, the ecosystem for server-side stuff barely exists.
And in my opinion, the ecosystem is one of the deciding factors when picking a language.
The move to make swift run on Linux could be part of that work - their sysadmins, devops, and backend folks probably use Vim/VSCode and deploy on Linux.
We might see the release of a very good backend framework by apple for Swift that works on Mac/Linux.
The new macros they added kinda bolt on serde into the language too - and the distributed actor stuff is basically RPC in the language.
They’re also using embedded Swift in the Secure Enclaves and other small processors due to its safety, though I think they have an extra layer of automated security checking that they use internally that doesn’t exist in the public builds.
But in short, yes. I believe they’re trying to use Swift for everything at this point and getting away from C/C++/whatever even for kernel work.
Just like Rust didn’t invent not using garbage collection.
- Built for embedded devices and already has RISC-V support
- Already has WASM was a compilation target
- Not just an LSP but amazing VSCode tooling
- Native C, C++, Java, Rust and JavaScript interop and a .NET bridge is in the works it seems.
- Typed "throws"
- Static linking (in development)
- Data-race free by default
Unique features to Swift:
- Distributed actors for concurrency that run on distant machines. (This is doable but not native to the language)
- non-copy RAII generic types (not actually sure what this is so I assume it’s not in Dart)
dart doesn't do C++ interop
https://github.com/dart-lang/language/blob/main/working/333%...
I think there will need to be exceptions to a strict race-free model eto not take away from dart‘s current capabilities, and without forcing people into something like a borrow checker. Swift has `@unchecked Sendable` which lets you tell the compiler "I promise I'll take care myself to keep everything in there race-free".
What I‘ve been missing in Swift are tools that let me add safety _within_ such @unchecked Sendables. There's the Swift Atomics lib: https://www.swift.org/blog/swift-atomics/, but its quite limited in its capabilities. So for now, I find myself reaching for tools provided by the community, like pointfree's LockIsolated: https://github.com/pointfreeco/swift-concurrency-extras/blob....
Recently went with using Kotlin Native over Swift on macOS for this reason, the C ABI export and C interop were painless.
C should already be pretty easy, though I haven’t really used it myself. I only use the Objective-C stuff.
I know they’ve been working on C++ but don’t have any experience to comment. Someone else had a nice comment listing resources on that.
It is only C++ interop that is more experimental/in-progress.
ARC is a GC, and a slower one than most traditional tracing GC's.
They’re both forms automatic memory management, but they work in very different ways.
Second, yes, ARC is a GC method. But when the language says "no garbage collector", they just mean no traditional (tracing) GC runtime because, for better or worse, tracing GC is what people think of when they hear "garbage collector".
Not really no. Look at go, D, nim, or any ahead of time compiled language.
> ARC is key in providing Swift its ability to maintain its low memory footprint compared to, say, Java
Again not really. Java has other issues. Like lack of value types, conservative escape analysis, needing extra memory for profiling and the jit, and HotSpot’s GCs are focused on server applications and are designed not to release memory until they’re about to run out.
Even if this were true ARC trades memory for execution speed. I can’t find the exact paper right now but the ixy implementations showed that you end up spending an unhealthy amount of time just counting references when you stay in pure swift code. I think it was somewhere > 30%.
No tracing gc. Ref counting is the other kind of garbage collector.
Can you share an example of this? It sounds really interesting but I couldn’t find any references. Do you mean the parameter “packs” features?
This is a great assessment. There are some shortcomings in go that I wish weren't and I'm kind surprised that people having decades of language design experience under their belt ended up designing go.
I'm looking for a way to generate server stubs from OpenAPI specs. If that, I'm all set with Vapor+OpenAPI and would retire go for my container adventures.
I don't have experience with OpenAPI, but is this what you're looking for? https://www.swift.org/blog/introducing-swift-openapi-generat...
I'm not sure what you mean by this, could you elaborate?
Sure Swift itself has some sharp edges, but not any more (or worse) than many other popular languages.
- if you want to manager your Package.swift target, you mostly have to deal with .target enum (can't figure out how to paste the URL)
You can really only do a define or unsafeFlags, which leads to a couple issues...
1) There's only Debug and Release configurations! What if you want more? What if you want a nice flow where you emit and consume pgo data and re-ingest it?
2) What if you want to use a define for another language managed by SPM, such as metal?
3) What if you want to put in a portable path into a linker argument? SPM blocks use of build-time environment variables, so you can't do that either.
All of these things seem contrived, but I ran into all three of them at my current job (shameless NanoFlick plug). All of these can be handled by cmake or xcodebuild (although probably better to use rules_xcodeproj).
- Swift compiler feedback and performance in builders is absolutely atrocious, to the point where binary searching a view to find the actual source of a type error over ~30 second build times isn't very unusual!
- It's very overly conservative with build times, and because there's only module-level import, it's very easy to accidentally have a long dependency chain which makes building one file imply building many files, further tanking your build time. There are some forum posts I can dig up on this if you're curious more on this point.
- POD structs are default-Sendable at module-level visibility, but lose that if they're public, which means if you try modularizing your app into different small packages to restore some notion of compilation units to fight against the above two points, you end up having to go around and mark all of your different classes with default protocols (usually just Sendable) to reimplement!
- Not a huge deal, but no way to have macro_rules! like you can in rust: macros are kinda hard to use and have to be in another package.
This is just my thoughts on tooling, I have many more thoughts about the language itself that I'd be happy to share.
Honestly though it's much better with vscode. I do hope that it makes progress in the right direction, Swift has a lot of things going for it!
Ignoring the Accept-Encoding header is common but sending gzip compressed response body and _also_ ignoring the header, thereby making it impossible for the client to disable compression using Accept-Encoding: identity, is relatively rare.
Another example that comes to mind is www.amazon.com. However in that case one can disable compression by sending a Viewport-Width header.
They require a value of "deflate, gzip" for Accept-Encoding.
Other sec.gov subdomains do not have this restriction.
FTC, who also uses Akamai, does not have it.
Weird.