pl-comparison
pl-comparison copied to clipboard
Comparison of widely used programming languages
Programming Language Comparison Cheat Sheet
Pros and cons of widely used programming languages from a technical point of view.
- Performance
- Safety
- Type system
- Polymorphism
- Pointer
- Memory safety
- Concurrency
- Tooling
- Terminology
- References
Performance
| Language | Compiled/interpreted | Process virtual machine (VM) | Garbage collector (GC) | Build time |
|---|---|---|---|---|
| Javascript | JIT | Yes (web browser, Node.js) | Yes | - |
| Typescript | JIT | Yes (web browser, Node.js) | Yes | - |
| Dart | JIT/AOT | Yes (web) / No (mobile) | Yes | - |
| Python | interpreted | Yes | Yes (reference counting) | - |
| Java | JIT | Yes (JVM) | Yes (tracing) | faster |
| Go | AOT | No | Yes | faster |
| Rust | AOT | No | No | slower |
| C++ | AOT | No | No | slower |
| C | AOT | No | No | slower |
Safety
| Language | Memory safety | Type safety | Null safety | Data race safety |
|---|---|---|---|---|
| Javascript | Yes (GC) | weak | No | Yes (single-threaded) |
| Typescript | Yes (GC) | strong (in strict mode) | Yes | Yes (single-threaded) |
| Dart | Yes (GC) | strong | Yes | Yes (single-threaded) |
| Python | Yes (GC) | strong | No | Yes (single-threaded) |
| Java | Yes (GC) | strong | No | No |
| Go | Yes (GC) | strong | No | Yes |
| Rust | Yes (RAII, ownership) | strong | Yes | Yes |
| C++ | Partially (RAII) | weak | No | No |
| C | No | weak | No | No |
Type system
| Language | Nominal/Structural | Static/Dynamic | Manifest/Inferred | Sum types |
|---|---|---|---|---|
| Javascript | - | dynamic | inferred | No |
| Typescript | structural | static | manifest | Yes (discriminated union) |
| Dart | nominal | static | gradual | No |
| Python | nominal | dynamic | inferred / optional manifest | No |
| Java | nominal | static | manifest / optional inferred | Yes (sealed classes) |
| Go | partially structural / partially nominal | static | partially inferred | No |
| Rust | mostly nominal | static | manifest / optional inferred | Yes (enum) |
| C++ | nominal | static | manifest / optional inferred | No |
| C | nominal | static | manifest | No |
Polymorphism
Four types of poylmorphism:
- Ad hoc polymorphism (overloading)
- Parametric polymorphism (compile-time polymorphism)
- Subtype polymorphism (runtime polymorphism)
- Nominal subtyping (interface inheritance)
- Structural subtyping (duck typing)
- Coercion polymorphism (implicit or explicit casting)
| Language | Ad hoc | Parametric | Subtype | Coercion |
|---|---|---|---|---|
| Javascript | No | No | Yes (duck typing) | Yes |
| Typescript | Yes | Yes | Yes | Yes |
| Dart | No | Yes | Yes | Yes |
| Python | No | Yes | Yes (duck typing) | Yes |
| Java | Yes (overloading) | Yes | Yes | Yes |
| Go | No | Yes | Yes | Yes |
| Rust | Yes (traits) | Yes (bounded, generics) | Yes (trait objects) | Yes (type coercions) |
| C++ | Yes (overloading) | Yes (templates) | Yes | Yes |
| C | No | No | No | Yes |
Pointer
| Language | Raw pointer | Smart pointer | References |
|---|---|---|---|
| Java | No | No | Yes (can be null) |
| Rust | Yes (unsafe Rust) | Yes (std::boxed::Box, std::rc::Rc, std::sync::Arc) |
Yes (can't be null) |
| C++ | Yes | Yes (std::unique_ptr, std::shared_ptr) |
Yes (can't be null, can't be reseated) |
| C | Yes | No | No |
Memory safety
| Language | Double free | Use after free (dangling pointer) | Null dereferencing | Buffer overflow |
|---|---|---|---|---|
| Java | No (GC) | No (GC) | Yes (NullPointerException) |
No |
| Go | No (GC) | No (GC) | Yes (nil pointer dereference) |
No |
| Rust | No (ownership rules) | No (borrowing rules) | No (std::option::Option) |
No |
| C++ | Yes | Yes | Yes | Yes |
| C | Yes | Yes | Yes | Yes |
Concurrency
| Language | Async/await | Multithreading | Channels |
|---|---|---|---|
| Javascript | Yes | Yes (web workers) | No |
| Typescript | Yes | Yes (web workers) | No |
| Python | Yes (asyncio) | Yes (multiprocessing with IPC; thread and threading but GIL) | No |
| Java | Yes | Yes (java.lang.Thread) |
Yes (java.nio.channels.Channels) |
| Go | Yes (via channels) | Yes (goroutines) | Yes (chan) |
| Rust | Yes (async-std, tokio) | Yes (std::thread, tokio, rayon) |
Yes (std::sync::mpsc) |
| C++ | Yes (std::async) |
Yes (std::thread, std::execution) |
No |
Tooling
| Language | Package manager | Toolchain management | Formatter | Linting | Testing |
|---|---|---|---|---|---|
| Javascript | npm, yarn, pnpm |
nvm, nvs |
prettier, beautifier |
eslint |
jest, mocha |
| Typescript | npm, yarn, pnpm |
nvm, nvs |
prettier, beautifier |
eslint |
jest, mocha |
| Python | pip, venv, setuptools |
pyenv |
autopep8, black |
mypy, pyright |
pytest |
| Java | maven, gradle |
-- | various | spotbugs |
JUnit, mockito |
| Go | go |
go |
go fmt |
go vet |
go test, testify |
| Rust | cargo |
rustup |
rustfmt |
clippy |
cargo |
| C++ | vcpkg, conan |
-- | clang-format |
clang-tidy |
-- |
Terminology
Type safety (or type soundness): The extent to which a programming language discourages or prevents type errors.
Polymorphism: A common interface to entities of different types.
Nominal type system: A name-based type system.
Structural type system: A property-based type system.
Type inference: Implicit typing.
Manifest typing: Explicit typing.
Static type checking: Verifying the type safety at compile-time.
Dynamic type checking: Verifying the type safety at runtime.
Strong typing: Stricter typing rules at compile-time.
Weak typing: Looser typing rules at compile-time.
Duck typing: If it walks like a duck and it quacks like a duck, then it must be a duck.
Data race: Two or more threads concurrently accessing a location of memory; one or more of them is a write; one or more of them is unsynchronized.
Memory safety: The extent to which a programming language protects from memory errors.
Buffer overflow: An anomaly where a program, while writing data to a buffer, overruns the buffer's boundary and overwrites adjacent memory locations.
Dangling pointer (or wild pointer): Pointers that do not point to a valid object. Null pointer, uninitialized pointer, and stale pointer are special cases of a dangling pointer.
Double free: Calling free twice on the same memory address.
Use after free: A dangling pointer that is used after it has been freed.
Null dereferencing: Dereferencing a null pointer.
References
- Wikipedia, Comparison of programming languages
- Wikipedia, Comparison of programming languages by type system
- Peteris Krumins, The Four Polymorphisms in C++
- Steve Klabnik and Carol Nichols, The Rust Programming Language