Slide with text: “Rust teams at Google are as productive as ones using Go, and more than twice as productive as teams using C++.”
In small print it says the data is collected over 2022 and 2023.
Slide with text: “Rust teams at Google are as productive as ones using Go, and more than twice as productive as teams using C++.”
In small print it says the data is collected over 2022 and 2023.
Hmm… I think the Rust-y answer to that problem is the same as the Haskell-y answer, “Use the Types!”. I.E. in the example above instead of returning an
i32
you’d return aNonZero<u32>
, and your args would bea: &NonZero<u32>, b: u32
. Basically make invalid state unrepresentable and then you don’t need to worry about the API being used wrong.I’m more referring to a more general application, such as:
fn do_stuff() -> Result<...> { if condition { return Error(...) } return Ok(...) } out (r) { if r.is_err() { // special cleanup (maybe has access to fn scope vars) } }
That gives you some of the
scope(failure)
behavior, without as many footguns. Basically, it would desugar to:fn do_stuff() -> Result<...> { let ret = if condition { Error(...) } else { Ok(eee) }; if ret.is_err() { ... }
I’m not proposing this syntax, just suggesting that something along these lines may be interesting.
I think the issue with that is that it’s a little bit of a solution in search of a problem. Your example of:
fn do_stuff() -> Result<...> { if condition { return Error(...) } return Ok(...) } out (r) { if r.is_err() { // special cleanup (maybe has access to fn scope vars) } }
isn’t really superior in any meaningful way (and is arguably worse in some ways) to:
fn do_stuff() -> Result<...> { if condition { // special cleanup (maybe has access to fn scope vars) return Error(...) } return Ok(...) }
For more complicated error handling the various functions on Result probably have all the bases covered.
For what it’s worth a lot of my day to day professional work is actually in Java and our code base has adopted various practices inspired by Rust and Haskell. We completely eliminated null from our code and use Optional everywhere and use a compile time static analysis tool to validate that. As for exception handling, we’re using the Reactor framework which provides a type very similar to Result, and we essentially never directly throw or catch exceptions any more, it’s all handled with the functions Reactor provides for error handling.
I just don’t think the potential footguns introduced by
null
andexception
s are worth it, the safer type level abstractions ofOption
andResult
are essentially superior to them in every way.Nice. We use Python and use
None
everywhere. I ranpyright
on our codebase, and while we use typing religiously, our largest microservice has ~6k typing errors, most of which are uncheckedNone
s. We also use exceptions quite a bit, which sucks (one thing really annoys me is a function likecheck_permissions()
which returns nothing, and throws if there’s an issue, but it could totally just return abool
. We have nonsense like that everywhere.I use Rust for all of my personal projects and love not having to deal with null everywhere. I’d push harder for it at work if others were interested, but I’m the only one who seems passionate about it (about 2-3 are “interested,” but haven’t even done the tutorial).
Yeah as far as I’m concerned
null
is public enemy number one. I refuse to work in any language that doesn’t allow me to indicate in some fashion that a variable is non-nullable. I just about had an aneurysm when I found out that JavaScript not only hasnull
, but alsonil
andundefined
and they all mean something subtly different. To be fair though, JavaScript is like a greatest hits of bad language design.JavaScript doesn’t have
nil
, but it hasnull
,NaN
andundefined
.But yeah, wrapping
null
in an Option is a really nice.It sort of has nil. While a type can be
null
orundefined
when evaluated,nil
is used in many of the JS libraries and frameworks to mean something that is eithernull
orundefined
. So you’ll see functions likefunction isNil(value) { return value == null || value == undefined }
and they’ll sometimes often confuse things even more be actually defining anil
value that’s just an alias fornull
which is just pointlessly confusing.As an aside, basically every language under the sun has
NaN
as it’s part of the IEEE floating point standard. JavaScript just confuses the situation more than most because it’s weakly typed so it doesn’t differentiate between integers, floats, or some other type like an array, string, or object. Hence anything in JS can be a NaN even though it really only has meaning for a floating point value.function isNil(value)
We instead have
function isNullOrUndefined(value) ...
instead, but it does the same thing.It’s especially lame since you can’t just do
if (!value) ...
since that includes 0 (but not[]
or{}
, which Python considers falsey). It’s remarkably inconsistent…Yup, but you can use
NotNan
in Rust, just like yourNonNull
example.And yeah, it’s weird that JavaScript doesn’t have an integer type, everything is just floating point all the way down. I actually did some bitwise logic with JavaScript (wrote a tar implementation for the web), and you get into weird situations where you need to
>>> 0
in order to get an unsigned 32-bit integer (e.g.(1 << 31) >>> 0
). Those hacks really shouldn’t be necessary…Because it’s floating point it also causes some REALLY strange bounds on integers. The maximum sized int you can safely store in JS is a 53 bit integer. That caused us all kinds of headaches when we tried to serialize a 64 bit integer and it started producing garbage results for very large values.