You seem to think of "rust enthusiasts" as some organized group with a goal of writing Rust for the sake of it. Rust is long past such extremely early adopter phase.
What you're seeing now is developers who are interested in writing a better version of whatever they're already working on, and they're choosing Rust to do it. It's not a group "Rust enthusiasts" ninjas infiltrating projects. It's more and more developers everywhere adopting Rust as a tool to get their job done, not to play language wars.
Nah, I called out redox and another commenter pointed out ripgrep as an even better example of what I’d prefer to see, and those are also by what I would call rust enthusiasts. I don’t think of them as a monolithic group.
Where we disagree is I would not call injecting rust into an established project “writing a better version”. I would love it if they did write a better version, so we could witness its advantages before switching to it.
The problem here is that C is too basic, dated, with inadequate higher-level abstractions, which makes writing robust and secure software extra difficult and laborious. "Learning underlying hardware" doesn't solve that at all.
Debian supports dozens of architectures, so it needs to abstract away architecture-specific details.
Rust gives you as much control as C for optimizing software, but at the same time neither Rust nor C really expose actual underlying hardware (on purpose). They target an abstract machine with Undefined Behaviors that don't behave like the hardware. Their optimisers will stab you in the back if you assume you can just do what the hardware does. And even if you could write directly for every logic gate in your hardware, that still wouldn't help with the fragility and tedium of writing secure parsers and correct package validation logic.
You've discarded the error type, which trivialised the example. Rust's error propagation keeps the error value (or converts it to the target type).
The difference is that Result is a value, which can be stored and operated on like any other value. Exceptions aren't, and need to be propagated separately. This is more apparent in generic code, which can work with Result without knowing it's a Result. For example, if you have a helper that calls a callback in parallel on every element of an array, the callback can return Result, and the parallel executor doesn't need to care (and returns you an array of results, which you can inspect however you want). OTOH with exceptions, the executor would need to catch the exception and store it somehow in the returned array.
Either works, but now you have two ways of returning errors, and they aren't even mutually exclusive (Either-retuning function can still throw).
Catch-and-wrap doesn't compose in generic code. When every call may throw, it isn't returning its return type T, but actually an Either<T, Exception>, but you lack type system capable of reasoning about that explicitly. You get an incomplete return type, because the missing information is in function signatures and control flow, not the types they return. It's not immediately obvious how this breaks type systems if you keep throwing, but throwing stops being an option when you want to separate returning errors from the immediate act of changing control flow, like when you collect multiple results without stopping on the first error. Then you need a type for capturing the full result type of a call.
If you write a generic map() function that takes T and returns U, it composes well only if exceptions don't alter the types. If you map A->B, B->C, C->D, it trivially chains to A->D without exceptions. An identity function naturally gives you A->A mapping. This works with Results without special-casing them. It can handle int->Result, Result->int, Result->Result, it's all the same, universally. It works the same whether you map over a single element, or an array of elements.
But if every map callback could throw, then you don't get a clean T->U mapping, only T -> Either<U, Exception>. You don't have an identity function! You end up with Either<Either<Either<... when you chain them, unless you special-case collapsing of Eithers in your map function. The difference is that with Result, any transformation of Result<T, E1> to Result<U, E2> (or any other combo) is done inside the concrete functions, abstracted away from callers. But if a function call throws, the type change and transformation of the type is forced upon the caller. It can't be abstracted away from the caller. The map() needs to know about Either, and have a strategy for wrapping and unwrapping them.
catch lets you convert exceptions to values, and throw convert values to exceptions, so in the end you can make it work for any specific use-case, but it's just this extra clunky conversion step you have to keep doing, and you juggle between two competing designs that don't compose well. With Result, you have one way of returning errors that is more general, more composable, and doesn't have a second incomplete less flexible way to be converted to/from.
I think you're missing the key point about return types with checked exceptions.
`int thing()` in Java returns type `int`. `int thing() throws AnException` in Java returns type `int | AnException`, with language-mandated destructuring assignment with the `int` going into the normal return path and `AnException` going into a required `catch` block.
The argument you're making, that the compiler doesn't know the return type and "you lack type system capable of reasoning about that explicitly", is false. Just because the function says its return type is `int` doesn't mean the compiler is unaware there are three possible returns, and also doesn't mean the programmer is unaware of that.
The argument you are making applies to UNchecked exceptions and does not apply to CHECKED exceptions.
It's not a single return type T that is a sum type. It's two control flow paths returning one type each, and that's a major difference, because the types and control flow are complected together in a way that poorly interacts with the type system.
It's not `fn(T) -> U` where U may be whatever it wants, including Ok|Exception in some cases. It's `fn(T) -> U throws E`, and the `U throws E` part is not a type on its own. It's part of the function signature, but lacks a directly corresponding type for U|E values. It's a separate not-a-type thing that doesn't exist as a value, but is an effect of control flow changes. It needs to be caught and converted to a real value with a nameable type before it can work like a value. Retuning Either<U, E> isn't the `U throws E` thing either. Java's special alternative way of returning either U or E is not a return type, but two control flow paths returning one type each.
Compiler is fully aware of what's happening, but it's not the same mechanism as Result. By focusing on "can this be done at all", you miss the whole point of Result achieving this in a more elegant way, with fewer special non-type things in the language. Being just a regular value with a real type, which simply works everywhere where values work without altering control flow is the main improvement of Result over checked exceptions. Removal of try/catch from the language is the advantage and simplification that Result brings.
Result proves that Java's special-case exception checking is duplicating work of type checking, which needlessly lives half outside of the realm of typed values. Java's checked exceptions could be removed from the language entirely, because it's just a duplicate redundant type checker, with less power and less generality than the type checker for values.
That would be true if the society was already perfectly fair and neutral (which some people believe).
However, there is racism and sexism in the world (it's systemic, in a sense it's not about one person not liking another personally, but biases propagated throughout the society). To counter that, you need to recognize it, and it will be necessary to treat some people differently.
For example, women may not feel safe being a small minority at a gathering full of men. If you do nothing, many potentially interested women will not show up. You could conclude that it's just the way things are and women are simply not interested enough in the topic, or you could acknowledge the gender-specific issue and do something about it. But this isn't a problem affecting everyone equally, so it would require treating women specially.
OTOH Leaf is a proof that old batteries can be replaced and upgrade an old car.
Original Leafs were sold with a 24kWh capacity. Current ones have 48kWh for the same price, and 64kWh replacement batteries are available. So you can go from half of the crappiest range to 3× more range than when the car was brand new.
Old batteries with reduced capacity don't even have to be thrown out. There are projects that reuse old Leaf batteries for grid energy storage (any capacity is useful when they're sitting on the ground).
I'm betting that the current-gen mainstream cars will benefit similarly, especially that production volume is orders of magnitude higher now (lots of brands share the same platform and battery modules).
It's functionally tolerable when you disable transparency and increase contrast in accessibility settings.
Of course it makes everything look dull and primitive. Crammed and misaligned controls are even more obvious when elements have borders. You still have unhelpful animations.
Advancements in lighting can help all games, not just AAA ones.
For example, Tiny Glade and Teardown have ray traced global illumination, which makes them look great with their own art style, rather than expensive hyper-realism.
But currently this is technically hard to pull off, and works only within certain constrained environments.
Devs are also constrained by the need to support multiple generations of GPUs. That's great from perspective of preventing e-waste and making games more accessible. But technically it means that assets/levels still have to be built with workarounds for rasterized lights and inaccurate shadows. Simply plugging in better lighting makes things look worse by exposing the workarounds, while also lacking polish for the new lighting system. This is why optional ray tracing effects are underwhelming.
What you're seeing now is developers who are interested in writing a better version of whatever they're already working on, and they're choosing Rust to do it. It's not a group "Rust enthusiasts" ninjas infiltrating projects. It's more and more developers everywhere adopting Rust as a tool to get their job done, not to play language wars.
reply