Fortran vs Rust vs Zig
Is Fortran a system language?
No — and the distinction is precise and useful. A systems language in the modern sense is one built to write operating systems, kernels, device drivers, browsers, embedded firmware and security infrastructure: it must provide manual memory control, small runtime, predictable machine-level behaviour, and (increasingly) compile-time memory safety. That world belongs to C, C++, Rust and Zig. Fortran was designed for formula translation: dense arithmetic over arrays, compiled to machine code as fast as the compiler can manage.
Three facts mark the boundary honestly:
- It compiles to native code and runs bare-metal fast — that part of "systems" it shares. On pure array numerics it often beats C, because whole-array expressions plus the no-aliasing rule give the optimizer more room.
- It has no memory-safety guarantees. Buffers can overflow, pointers can dangle. Fortran catches many classes with
-fcheck=bounds, but nothing enforces safety at compile time the way Rust's borrow checker does. - Its ecosystem ignores systems territory. Nobody builds kernels, drivers or embedded firmware in Fortran; the tools, CRT bindings and libraries for that work simply are not there.
Conclusion: Fortran is best described as a high-performance numerical/scientific language, close to the metal in its own domain, not a general-purpose systems language. Choosing it for HPC kernels is correct; choosing it to write an OS would be a category error. The comparison page's landscape figure summarizes the positions.
Fig. 1 — Languages aim at different axes; Fortran owns the far numerical end.
Head-to-head: Fortran, Rust, Zig
The table below compares by the properties engineers actually choose on. "Guarantee" means what the language promises at compile time; the last row is where Fortran is decisively different from both.
| Property | Fortran (2023) | Rust | Zig |
|---|---|---|---|
| Born / decade | 1957, revised continuously | 2015 | 2016 |
| Primary domain | numerics, HPC, simulation | systems, safe infrastructure | systems, C replacement |
| Memory safety | none enforced at compile time | guaranteed (borrow checker) | opt-in safety, allocators explicit |
| Whole-array math | native (A = B + C) | via iterators/manual loops + crates | manual loops |
| SIMD/vectorization | compiler-driven, standard | std::simd/autovectorization improving | explicit, low-level |
| Parallelism, standard-level | coarrays + do concurrent | threads/std + rayon (lib) | threads + std, manual |
| Distributed HPC (MPI) | native binding, century of practice | rsmpi (thin binding) | manual FFI |
| Compile-time metaprogramming | limited (generics, preprocessor) | macros (powerful) | comptime (unique) |
| Interop with C | iso_c_binding, ABI-standard | extern "C", cbindgen | @cImport, first-class |
| Ecosystem maturity in numerics | BLAS/LAPACK/netCDF/HDF5, 50 years | young (nalgebra, ndarray) | very young |
| When to reach for it | the hot kernel, the sim, the solver | the service, the parser, the security boundary | the driver, the embed, the C interop layer |
What Rust and Zig give up for numerics
Rust's safety and Zig's control are real and valuable — nobody recommends Fortran for a network daemon. But in the numerical arena, each pays a price Fortran visitors notice immediately:
- Rust and arrays: whole-array expressions are not part of the language.
a[i] = b[i] + c[i]loops are written by hand or via iterators; the optimizer recovers much of it, but the expression is the loop and the loop is yours to write and maintain. SIMD arrives via unstable std::simd or crates, not via a-O3flag. - Zig and vectors: Zig gives explicit control of memory and
comptimecode generation — superb for allocators and kernels — but array math stays manual, and HPC libraries (BLAS/LAPACK/MPI bindings) are near-zero. You would build the wheel before the physics. - Both and aliasing: Rust's aliasing rules are powerful but you pay for them with verbosity in numerical code (raw pointers, unsafe blocks, iterator gymnastics); Zig's default is C-like aliasing that still forces the optimizer's hand less than Fortran's no-alias default.
None of this is a failure of Rust or Zig — they chose different guarantees. The point for you as an engineer is to not make one language carry the whole product if the product has two very different ends.
The hybrid: Fortran kernels inside a modern system
The interop lesson's ABI is the bridge that makes the either/or disappear. A realistic modern stack: Rust or Go service owns the API, auth, orchestration; Fortran shared library compiled with bind(c) owns the physics; Python wires experiments and models on top. Each layer uses the language it cannot be replaced in, and the C ABI (or f2py) is the only glue. The python-interop lesson demonstrated the same pattern from the Python side; the interop demo shows the raw bind(c) symbol table.
# the build that produces the cross-language kernel library
gfortran -c -O3 -fPIC physics.f90
gfortran -shared -o libphysics.so physics.o
# Rust service: extern { fn solve(...); } Zig: @cImport libphysics.so
Picking the right side
Decision rules distilled from the whole lesson:
- Floating-point-heavy, array-shaped, cluster-bound → Fortran (with OpenMP/MPI/coarrays as the terrain allows).
- Systems component, security-critical, massive concurrency → Rust.
- Embedded, allocator-level control, C ecosystem drop-in → Zig.
- Mixed product → ship the kernel as a library and host it anywhere the target demands.
The honest closing note: language loyalty is cheap, performance is priced in physics, and the best engineers in this field read the guarantees, measure, and pick without ideological baggage.