I just wrote `task.join()??`. Am I bad at #rust?
@rillian you got me curious what the record for the longest `?` operator chain on crates.io is
nix/src/mount/linux.rs at 154a8a4a00fea72ada265de69485b67634fb49b9 · nix-rust/nix

Rust friendly bindings to *nix APIs. Contribute to nix-rust/nix development by creating an account on GitHub.

GitHub
@rillian @antonok Rust beginner here, what does ???? do in this context?

@wertercatt

"what is ???? ????" 😉

It's 4 chained `?` operators. That's a shorthand to convert a type like `Result<Result<Result<Result<T, _>, _>, _>, _>` into just `T` if all the results are successful. Otherwise if any one of them is an error, the error gets returned from whatever function this code is inside.

@wertercatt @antonok If you wanted lower-level, the rust standard library has a `Result` enum which is often used for the return value of functions. Its variants represent either success, wrapping the actual value, or an error. Putting `?` in an expression unwraps successful results, or returns if it's an error. So errors bubble up, but the success path stays tidy.

So `??` just unwraps nested Results: a result of a result if you will.

See https://doc.rust-lang.org/book/ch02-00-guessing-game-tutorial.html#handling-potential-failure-with-result and https://doc.rust-lang.org/book/ch09-02-recoverable-errors-with-result.html

Programming a Guessing Game - The Rust Programming Language

@antonok @rillian oh hell, it's like LISP but they used the question mark as an operator rather than for text literals. The pain: it hurts.

@me @antonok @rillian it's actually very nice in most cases, so if you want to bubble up errors you just write

function1()?;
funftion2()?;

the issue in this snippet is that it's all nested closures, making that all a bit harder

@laund @antonok @rillian would it not have been more clear in this example to do something like:
} )?
} )?
} )?
} )?;
Not sure how the operator is scoped so maybe not, but generally speaking people are much better at reading one question mark than a row of them.
@me @antonok @rillian the issue is that you can't return from inside a closure, since the closure might be called later/elsewhere
@antonok But I don't think it works that way - an inner Err() will cause the calling function to return Err() - so I think only one ? is needed because it will bubble up the chain. In any event, it doesn't seem idiomatic, as normally you would apply ? right after the call that could fail, unless there is a specific reason for not yielding back at that moment.
@antonok I wonder if `with_nix_path` can return a RAII wrapper instead, to avoid the closure?
@antonok @rillian wow, this is something. Though I almost feel like one `?` should handling 1..n cases of nested `Result<Result<…,T>>`, unless you’d need to pass one of the deeper nested Result’s around somewhere? maybe it’s not so simple
@mattjbones @antonok @rillian You'd almost certainly need support for algebraic effects or algebraic union types for that unfortunately.
@antonok @rillian it's called nix-rust but it doesn't have any ?
@antonok what lack of monads does to a mf
Result in std::result - Rust

`Result` is a type that represents either success (`Ok`) or failure (`Err`).

rustfmt/tests/target/chains.rs at a1fabbf3865c1044c7e96e8ac337131ebdf411b6 · rust-lang/rustfmt

Format Rust code. Contribute to rust-lang/rustfmt development by creating an account on GitHub.

GitHub
@val @rillian it might not compile, but excellent find nonetheless 🏆
@antonok @rillian well, I just finished grepping a crates.io mirror, and there doesn't seem to be any longer ? chain than 4 (besides rustfmt)

@antonok Looks a bit overengineered tho. This would be much simpler:

let s = source.map_or(std::ptr::null(), |x| x.as_ptr());
// other params
let res = unsafe libc::mount(...)?;