4 ms·
That’s why I said properly implemented! Good optional implementations let you do the check once to unwrap the inner value, so you have one branch. In rust:
by emtel 5y ago
That’s why I said properly implemented! Good optional implementations let you do the check once to unwrap the inner value, so you have one branch.
In rust:
fn foo(s: Option<&S>) -> {
if (let Some(inner) = s) {
// now you can access inner repeatedly
// without branches
}
}
(edit to fix syntax errors)
- jcelerier 5y agobut this declares a new variable - I find this super messy and really decreases readability in practice since now it's not obvious anymore that what you're working with was a function parameter. Also it adds a scope level - I much prefer the if(!bla) return; // use bla early-return style. So that's really a no-go for me from years comparing the two styles
- emtel 5y agoWell if you have aesthetic objections to the way rust does it, I can't argue with you. Kotlin does it the way you like, though. Anyway, I started in on this thread because I was objecting to the claim that optional types always have overhead. They don't. That's all I wanted to show.
- matvore 5y ago> Well if you have aesthetic objections to the way rust does it, I agree with GP that it is harder to maintain ("messy" they say) if there is more than one variable referring to the same value. It is not so subjective as you make it to be.
- iudqnolq 5y agoThe idiomatic way to solve that in rust is to re-bind to the same variable name. if let Some(foo) = foo { /* ... * / } That's possible because in Rust name shadowing let foo = grab_foo_bytes(); let foo = parse_foo_bytes(foo); makes the previous binding of the variable no longer namable and thus no longer accessible, but doesn't drop it (and trigger RAII destructors). Now someone will probably come in and say "oh no, this isn't exactly like c, how will anyone ever understand it". To that I reply why is it that c users get to say "if you don't know how c works exactly you're holding it wrong" and then comment about other languages "I don't want to have to learn anything to hold it right".
- matvore 5y ago> The idiomatic way to solve that in rust is to re-bind to the same variable name. OK, that's reasonable. Is the idiomatic way to use optionals to introduce a layer of nesting? I prefer keeping functions very "flat"-looking. It sounds like Rust's optionals will give people an excuse to create labyrinthine functions where I'm constantly scrolling around to remind myself of what level of nesting I'm at and whether I'm in a loop or not, etc. As you say, you don't need a new block to shadow a previous var, so hopefully that style catches on. > "oh no, this isn't exactly like c, how will anyone ever understand it" Not very persuasive, sure, but the network effect of the C/C++ culture (including its general syntax and imperative nature) is a strength in and of itself. New languages would do well to coddle the existing C++, Java et. al. users wherever it doesn't contradict the language's central mission.
- iudqnolq 5y ago> I prefer keeping functions very "flat"-looking. I definitely agree. One way I do that is by having an internal function that takes a valid value and a public function that does the validating/error handling. That doesn't always make sense though. There's a few other idiomatic ways to avoid nesting. Since statements evaluate to values, you can write let foo = if let Some(foo) = foo { foo } else { // Something that either evaluates to the same type as foo or returns early } That's so common there's a special operator for it, ?. It essentially either early returns the sad path or evaluates to the happy path. fn get_foo() -> Option<Foo>; fn frob() -> Option<Bar> { let foo = get_foo()?; let bar = convert_to_bar(foo); Some(bar) } I prefer to use Result to model missing data like cases instead of Option because it composes better. So that might be fn get_foo() -> Option<Foo>; fn frob() -> Result<Bar, BarNotFound> { let foo = get_foo().ok_or(BarNotFound)?; let bar = convert_to_bar(foo); Some(bar) } #[derive(Debug, thiserror::Error)] #[error("Bar not found")] struct BarNotFound; That last bit uses a stdlib macro and a very commonly used external lib macro to save a few lines of repetitive typing. Edit: Also ? doesn't special case Result and Option. You can make your own type conform to the interface (trait) it requires. That would probably be weird though.