Type hints, ownership and Rust: how much should the compiler check?
Guido van Rossum explains why Python added optional type hints, Chris Lattner builds types and ownership into Mojo, the FFmpeg maintainers debate Rust, and ThePrimeagen compares languages from daily use. The discussion helps programmers decide how much safety to trade for speed of writing.

The Short Version
There is no single right amount of compiler checking, and the people who build and use these languages disagree about where the line belongs. Guido van Rossum kept Python's type hints optional and unchecked at runtime, because the language's appeal to beginners rests on its dynamic nature [1]. Chris Lattner keeps typing optional in Mojo too, but says that once you declare a type the compiler should enforce it and use it for speed, and he adds ownership so values are transferred instead of copied [2]. The FFmpeg and VLC maintainers accept memory safety as a goal but will not trade away handwritten assembly, testing discipline or a working codebase to get it [3]. ThePrimeagen, who uses several languages, likes what Rust's ownership gives him and still reaches for Go, Zig and runtime asserts, which suggests that checking is something you choose per project, not once for life [4].
Table of contents
What They Said
Guido van Rossum: hints that the interpreter ignores
Van Rossum's starting point is that Python has no declarations at all. In Python, he says, you do not generally declare the types of your variables or even their existence: "They just spring into existence when you first assign them" [1]. He sees that as a real benefit to newcomers, because "there is less bookkeeping" to learn before they can start playing with code [1]. A first programming course already has to cover algorithms, data structures, loops, functions and recursion, so he wants beginners spending less time fighting the language [1].
The price is speed. Because the interpreter cannot read types from the source, a generic add must find out at run time whether it is adding integers, floats, strings, lists or user-defined objects, and do error checking every time [1]. Python 3.11's answer was not to demand types but to watch what types actually turn up at a given line and swap in a specialized instruction, with a fallback to the generic path if the guess is wrong [1]. Van Rossum describes the aim as making interpretation more efficient "without losing the super dynamic nature of the language" [1].
Type hints, introduced by PEP 484, sit on top of that design. "Type hints is an optional mechanism that people can use," he says, and it is especially popular at larger companies with very large Python codebases [1]. Asked whether hints are just documentation, he answers: "More than documentation." [1] They form a sub-language in which you can say that a variable is an integer, an argument is a string, or a function returns a list of strings [1]. Then comes the key limit: "But that's not checked when you run the code." [1] A separate static type checker reads the code without executing it and flags inconsistencies, like a linter focused on types [1].
Why not have the interpreter enforce or exploit the annotations? Van Rossum gives two reasons. Annotations sometimes contain what the summary of the conversation calls "lies," and enforcing them would break many dynamic programs [1]. In his own words: "And some Python programs wouldn't even be possible because they're too dynamic." [1] So, he says, "we made a choice of not using the annotations" [1]. He leaves a door open: a few releases in the future, Python could treat annotations as hints for generating a specialized instruction while keeping the generic operation as a fallback, but, he adds, they are not there yet [1]. Meanwhile annotations can be inspected while a program runs, and third-party libraries can use that to enforce them, at a performance cost [1].
The ecosystem he describes is deliberately loose. Mypy was started by Jukka Lehtosalo; he and van Rossum met at a Python conference around 2013 and agreed on a syntax that needed no changes to Python, reusing existing annotation syntax [1]. Facebook later built Pyre in OCaml, Google built pytype for its monorepo, Microsoft built Pyright, and PyCharm has its own heuristic checker [1]. Type checkers release far more often than Python's yearly cycle, which makes them a laboratory of experiments, whereas syntax that enters the language cannot easily be removed [1]. Van Rossum believes many professional Python developers run a static type checker in continuous integration, most often mypy; a web survey Fridman found put type hint use at 20 to 30% of Python 3 codebases [1].
None of this makes him a skeptic of static checking. He would recommend TypeScript over plain JavaScript, because strict typing helps you keep your head straight about what your code does, and he credits TypeScript's compatibility with JavaScript libraries for its success [1]. His reason for caring is social: "software is a very social activity," written for other programmers as much as for the computer [1].
Chris Lattner: declare a type and the compiler holds you to it
Lattner begins by criticizing exactly the arrangement van Rossum describes. Python's type hints, he says, are ignored by the interpreter per the Python spec, interpreted differently by competing checker tools, and cannot be used for performance [2]. In Mojo, if you declare a type, the compiler enforces it [2].
But he does not conclude that everyone must write types. Mojo lets developers adopt them progressively, and Lattner rejects the idea that untyped Python is a "guilty pleasure" that has to be rewritten for serious work [2]. Prototypers should not feel pressured, while large teams may adopt house rules requiring types [2]. The language exposes the choice in its syntax: the def keyword works exactly as in Python, while fn is a stricter mode that requires declared variables, which appeals to systems programmers [2]. Fridman, for his part, admits he loves strictness and litters his personal code with asserts [2].
For Lattner the payoff of types is largely speed. CPython represents every value, even an integer, as a heap-allocated object with a header, reached through a pointer and reference counted; getting values out of the heap and into registers is, by his account, roughly a 10x gain on top of the 2x to 10x from moving from an interpreter to a compiler [2]. Mojo implements even integers and floats in the library, using static structs alongside dynamic classes, so user-defined types are never second-class [2]. He is appreciative of the work that made CPython 3.11 faster, but notes that it does not run GPUs or vectors, and quips: "20% good 35 000 times is better." [2]
Ownership is where Mojo goes furthest beyond Python. Mojo offers value semantics: collections behave as if you get a logical copy, which prevents bugs such as a database class holding onto a record that a coworker later modifies [2]. Python programmers usually resort to defensive copies, and PyTorch users must remember to clone tensors in the right place [2]. Mojo copies lazily, only when data is actually modified, and relies on an ownership system related to work in Rust and Swift so that values are transferred instead of duplicated [2]. Argument conventions let code borrow a reference without changing it or pass a mutable reference, which matters for types that cannot be copied or moved, such as atomic numbers and database handles [2]. Mojo also destroys values as soon as possible after their last use instead of at the end of scope as in C++ [2]. The feature Lattner says he is most excited about is lifetimes, a new approach to safe references without dangling pointers [2].
His strategy constrains all of this. Mojo is meant to be a superset of Python, because he learned while building Clang that it is far easier for engineers to implement long-tail compatibility than to persuade every programmer to rewrite their code [2]. Python, in his words, is not broken, just "not done yet." [2]
Jean-Baptiste Kempf and Kieran Kunhya: safety is the goal, but the bar is the existing code
The FFmpeg and VLC maintainers split between themselves on Rust, though less than the debate's reputation suggests.
Kunhya gives Rust its due first: "I think it's valuable what they're doing in terms of memory safety as a concept." [3] Then the reservations. Asked whether Rust can match the speedups of assembly, he says: "Not assembly by hand, no." [3] He says the language has an "Esperanto vibe" and compares it to the Sinclair C5: to get people to switch, something has to be as good as or better than what exists [3]. He would not object to Rust code in FFmpeg, but it has to work as well and support the same unit testing as everything else: "It needs to be flawless." [3] He objects to breaking ABI at will and notes that "it still has only one compiler implementation" [3]. And he concedes the shared aim, saying of memory safety that "we probably all agree that that's the goal" [3].
Kempf speaks as a user: "So I've done a ton of rust." [3] His startup Kyber is written mostly in Rust, and he says it is excellent for new projects [3]. His warning is about rewrites, since reading code is far harder than writing it [3]. He also points to a hole in the safety story for their kind of software. If you rewrite part of a C codebase in Rust and want the same performance, you will need inline assembly, "and so you destroy your whole security model" [3]. His proposed answer is assembly that is checked at compile time, instrumented to confirm it is not jumping anywhere in memory [3].
Their position makes sense only against what the code has to do. FFmpeg's SIMD assembly typically runs 10x to 50x faster than C, with one function shown at 62x, and both men reject the claim that compiler auto-vectorization can match it [3]. They do not even use intrinsics, C functions that leave register allocation to the compiler [3]. The AV1 decoder dav1d, whose motto was "every cycle matters," contains roughly 30,000 lines of C and 240,000 lines of handwritten assembly [3]. The stakes, as the maintainers put them, for software running on perhaps 100 million to a billion CPUs at once: "So every instruction matters." [3]
What they use in place of compiler guarantees is testing and review. FATE, FFmpeg's volunteer-hosted testing system, enforces bit-exact output across a huge matrix of operating systems, compilers and CPU architectures [3]. A core community of 10 to 15 people must maintain whatever a contributor leaves behind, the maintainers explain, so "It needs to be maintainable. It needs to be excellent." [3]
The GIST of every new summary in your inbox, daily or weekly. Free, and you can unsubscribe any time.
ThePrimeagen: choose per project, and add your own checks
Michael Paulson, known as ThePrimeagen, answers from daily use across languages. In Rust he loves that memory is freed automatically when the owning variable goes out of scope, without a garbage collector, and that mutability must be declared explicitly, which he says makes code easier to read [4]. Zig appeals to him as a middle ground between C and Rust, with optionals to prevent null pointer bugs and compile-time code written in the same language [4]. Go is his current workhorse for web backends [4].
He does not tell anyone to pick one camp. He suggests everyone explore a dynamic language like Python or JavaScript, a strict one like Rust or Go, and eventually a functional language [4]. For true beginners he recommends JavaScript, because you can quickly draw and move things on screen [4].
He also relies on checks no compiler performs. Inspired by Fridman, John Carmack and the creator of the TigerBeetle financial database, he adopted asserts that crash the program when an impossible state occurs [4]. TigerBeetle follows NASA-style rules, including at least two asserts per function that stay on in production, and runs deterministic simulation testing equivalent to hundreds of years of random input [4]. Trying this in his own proxy and game projects, Paulson found bugs he says he never would have caught otherwise [4].
Where They Agree, and Where They Don't
They agree that checking matters more as code and teams grow. Van Rossum says type hints are most popular at companies with very large codebases [1]. Lattner expects large teams to require types by house rule [2]. Paulson says he learns the most when a project reaches roughly 5,000 to 10,000 lines, where early design choices either weigh you down or free you [4]. The FFmpeg maintainers demand excellence because so few people maintain so much [3].
They agree that strictness should not be forced on beginners and prototypers. Van Rossum protects the lack of bookkeeping [1], Lattner refuses to treat untyped code as second-rate [2], and Paulson starts beginners in a dynamic language [4].
They agree that you cannot ask the world to rewrite its code. Van Rossum chose a hint syntax that required no changes to Python, and the painful Python 2 to 3 transition is why there will be no Python 4 anytime soon [1]. Lattner built Mojo as a superset for the same reason [2]. Kempf warns against rewrites [3]. Paulson stresses learning why existing code was written a certain way before rewriting it, and tells of deleting a colleague's odd-looking code only to reproduce it almost line for line: "I am the idiot in the situation." [4]
They disagree on whether a declared type should be binding. Van Rossum says no: enforcing annotations would break programs that are too dynamic, so checking stays in separate tools [1]. Lattner says yes: unenforced hints are read differently by competing checkers and buy no performance, so a declared type should be enforced by the compiler [2]. Each view follows from the job. Van Rossum is guarding a 30-year-old language and its existing programs [1]; Lattner is working backward from the limits of the hardware [2].
They disagree on what the compiler can deliver for performance. Lattner's case is that types, ownership and compilation unlock very large speedups [2]. Kunhya and Kempf say that for their inner loops no compiler, in any language, matches assembly written by hand, and the gap is multiples, not a few percent [3]. The two claims are about different baselines: Lattner compares with CPython [2], the FFmpeg maintainers with C [3].
They disagree on Rust itself. Paulson praises its ownership and explicit mutability [4]. Kempf uses it for new work but not for rewrites [3]. Kunhya doubts it has yet earned a switch [3]. Lattner borrows the ownership idea while keeping Python's syntax and compatibility [2].
Why It Works
Static checks catch inconsistencies before the code runs. Van Rossum describes a type checker as reading code without executing it and flagging inconsistencies [1]. He distinguishes typos, caught quickly, from thinkos such as forgetting to initialize a variable [1].
Types tell the machine what it can skip. Without them, Python's interpreter follows pointers to find the right add function and does error checking every time [1]. With a known integer type, a compiler can emit a single hardware add instruction, as van Rossum notes [1], and Lattner's unboxed values go straight into registers [2].
Ownership removes a class of sharing bugs and of copies. Value semantics prevent one piece of code from silently modifying data another piece holds, and ownership lets values be transferred instead of defensively copied [2]. Paulson's version of the benefit is memory freed when the owner goes out of scope, with no garbage collector [4].
Explicitness helps the human reader. Paulson finds declared mutability easier to read [4], and van Rossum's whole case for readability is that programs have two audiences, the computer and other programmers [1].
Where the compiler cannot see, tests and asserts take over. FATE checks bit-exactness across platforms [3]; asserts and simulation testing catch impossible states at run time [4]. Shared state under concurrency is the hard case van Rossum singles out, where all orderings of operations must be anticipated and "concurrency bugs are just harder." [1]
On evidence, the sources offer few measurements of bugs prevented. Van Rossum recalls research suggesting about one bug per thousand lines in mature software, and distrusts a vendor report with far larger numbers as marketing [1]. The firm numbers concern speed: Lattner's layered gains over CPython [2] and FFmpeg's 10x to 50x over C [3].
Where It Applies, and Where It Doesn't
Stricter checking pays off most in large shared codebases, in new projects, and where performance or hardware targets depend on known types [1][2][3]. Kempf's own choice of Rust for Kyber is the example of a clean start [3].
It pays off least in three places the sources name. The first is learning and prototyping, where less bookkeeping lets people get going [1][2]. The second is highly dynamic programs, some of which, van Rossum says, would not even be possible under enforced annotations [1]. The third is hand-optimized inner loops, where the work is done in assembly and the safety model of the surrounding language does not reach it [3].
A safe language is also not a full answer on its own. Kunhya's conditions for Rust in FFmpeg are about stability and testing, not about the type system [3]. And Paulson notes that a benchmark showing Zig beating other LLVM-based languages mostly reflects how the code was written, not the language [4].
Finally, some of what is described was unfinished when discussed. Mojo was about seven months old, launched as a 0.1 that was not for production, with traits, classes and lifetimes still to come [2]. Python's use of annotations for speed was only a possible future [1].
What To Do With It
- Start loose, tighten as the code grows. Prototype without types if that helps, as both van Rossum and Lattner allow [1][2], and adopt a house rule requiring them once a team and codebase are large [2].
- Run a static type checker in continuous integration for Python. Van Rossum believes many professional Python developers already do, most often with mypy [1].
- Do not treat Python hints as guarantees. They are not checked when the code runs [1]. If you need runtime enforcement, a third-party library can provide it, at a performance cost [1].
- Prefer TypeScript to plain JavaScript. Van Rossum recommends it because strict typing helps you keep track of what your code does [1].
- Learn one language from each camp. Paulson suggests a dynamic one such as Python or JavaScript, a strict one such as Rust or Go, and later a functional one [4].
- Use a strict language for new projects, and be wary of rewrites. That is Kempf's rule from using Rust himself [3]. Before replacing old code, find out why it was written that way [4].
- Hold new code in any language to the existing test bar. Kunhya's standard is that it must work as well and support the same unit testing as everything else [3].
- Add asserts for states that should be impossible. Paulson found bugs this way that he would not otherwise have caught, and TigerBeetle keeps its asserts on in production [4].
- Know where your safety model stops. If performance pushes you to inline assembly, Kempf warns, the guarantees of the surrounding language no longer hold, so that code needs its own checking [3].
Sources
- Guido van Rossum on Python 3.11's Speedup, Type Hints, the GIL and the Future of Programming · Lex Fridman Podcast
- Chris Lattner on Mojo, Modular and the Future of Programming and AI · Lex Fridman Podcast
- FFmpeg and VLC with Jean-Baptiste Kempf and Kieran Kunhya: Codecs, Assembly, and the Volunteers Behind Internet Video · Lex Fridman Podcast
- The Primeagen: Programming, AI, ADHD, Productivity, Addiction, and God · Lex Fridman Podcast