I've found myself in a context where I need to do a lot of array indexing, but where i simply ignore the case where it isn't present.
The None case is possible, so I can't use unsafe code here; i simply want to ignore it and return early
I end up with a lot of the following:
let Some(item) = some_vec.get(i) else { return; }
I would much prefer to be able to do something like
let item = some_vec.get(i)?;
Is there a way to have this work?
The only thing i can think of is to have an #[inline(always)] wrapper function that calls a private function that returns an Option, so I can use ? in that context.
fn inner(i: usize) -> Option<SomeResidual>;
pub fn outer(i: usize) {
inner(i);
}
// Some struct/residual impl so i can use ? on some generic Option<T>
That just feels clunky, though, and I would hope that there's a better way
If you're ok with nightly features, you can implement your own type that can be ?'d in functions returning ():
#![feature(try_trait_v2, never_type)]
use std::ops::{Try, FromResidual, ControlFlow};
// The `Try` type we'll be using to make `?` allow returning `()`
struct SomeOrReturn<T>(Option<T>);
// Boilerplate impl
impl<T> Try for SomeOrReturn<T> {
type Output = T;
type Residual = SomeOrReturn<!>;
fn from_output(output: Self::Output) -> Self {
Self(Some(output))
}
fn branch(self) -> ControlFlow<Self::Residual, Self::Output> {
match self.0 {
Some(value) => ControlFlow::Continue(value),
None => ControlFlow::Break(SomeOrReturn(None)),
}
}
}
// Boilerplate impl
impl<T> FromResidual<SomeOrReturn<!>> for SomeOrReturn<T> {
fn from_residual(_residual: SomeOrReturn<!>) -> Self {
Self(None)
}
}
// The magical impl that allows you to use `?` on `SomeOrReturn<T>`
// inside an `fn(...) -> ()`.
impl FromResidual<SomeOrReturn<!>> for () {
fn from_residual(_residual: SomeOrReturn<!>) -> Self {}
}
// Extension trait to go from an `Option<T>` to a `SomeOrReturn<T>`.
trait AsSomeOrReturn<T> {
fn some_or_return(self) -> SomeOrReturn<T>;
}
impl<T> AsSomeOrReturn<T> for Option<T> {
fn some_or_return(self) -> SomeOrReturn<T> {
SomeOrReturn(self)
}
}
The Try and FromResidual impls cause a fair bit of boilerplate, and I've also added an extension trait to make the interface easier, but you could stash this away in some utils module and just use the extension trait.
Then use it as such:
fn b() -> Option<usize>;
fn c() -> Option<usize>;
fn main() {
let b = b().some_or_return()?;
let c = c().some_or_return()?;
// ...
}
(See the playground)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With