rfcs
rfcs copied to clipboard
Make square bracket `[]` more usable.
TL;DR version: Make vec[key]_? = new_value;
doesn't panic when key is out of bounds.
For Rust syntax budget, square bracket is perhaps one of the least rewarding current design at all. Every occurrence of square bracket reminds people the potential possibility of panicking. This might be suitable for the "error early if something goes wrong" style application, but for long running services, propagating the panic to the root of the thread callstack might not be a good idea.
This proposes putting an _
(underscore) between the bracket and the ?
operator to invoke new TryIndex{,Mut}
traits instead, returning a Result
with an error type, then perform ?
operator on that to return early.
I generally like the idea of TryIndex{,Mut}
but I'm not really sold on $expr[$expr]_?
. Would $expr[$expr]_
also be allowed to enable for example $expr[$expr]_.unwrap_or($expr)
?
then perform
?
operator on that to return early
The problem being that expr?
is not a valid place, so if you treat x[y]_
and x?
as independent constructs then x[y]_? = z
will never compile.
Yes, i meant operationally first call such TryIndex{,Mut}
trait method, then call Try
trait method and early return etc. Syntactically it's regarded as a whole, not two independent constructs.
The Try
trait can't produce any "place" either. Unless you want it to return an &mut X
on success and make the lowering implicitly dereference the Try result.
let x: Vec<String>;
let y: usize;
let z: String;
x[y]_? = z;
// equivalent to
*(x.try_index_mut(y)?) = z;
// fn try_index_mut(&mut self, key: usize) -> Result<&mut String, SomeError>;
The implicit dereference means method chaining is not possible without context-dependent lowering rules
x[y]_.inspect_err(|err| warn!(err, "{y} did not exist"))? = z;
Interesting idea, but underscore looks ugly and inconvenient to type after ]
. Maybe something like ??
to not return immediately but to act like expression-level equivalent of ?
could work better?
let mut x = vec![1];
println!("{:?}", x[0]??); // Some(1)
println!("{:?}", x[3]??); // None
x[3]?? = 5;
println!("{:?}", x[3]??); // Some(5)
println!("{:?}", x[2]??); // ???
This could also be helpful be with maps:
let mut x = BTreeMap::<usize, usize>::new();
x[&3]?? += 5;
println!("{x:?}"); // { }
x[&3]?? = 5;
println!("{x:?}"); // { 3: 5 }
chained ?
is already a valid syntax (and used in real code), you can't repurpose ??
to mean anything else.
fn lol(huh: Option<Option<Option<Option<Option<()>>>>>) -> Option<()> {
huh????
}
you could do !? like in haskell, its a good compromise between the uglyness of _? and ?? already being taken.
let arr = [1,2,3];
arr[3]!?; // returns Option<{Integer}>
!
in Rust is already confusing enough (!matches!()
), please don't add even more uses to it
The older I get, the less important I feel syntax-centric discussions are. Here is my proposal, take it or leave it:
array[?index]
As far as I am aware of, that is not taken.
However, we might want to CC the Effects WG, since they might have their own ideas about this as this issue overlaps with their work. Should we continue the discussion on Zulip?
I propose the ¿?
syntax. That way we keep the style of surrounding brackets but make clear the operation may throw an error. assert_eq!(array¿5?, Ok(42))
and with the try operator let answer_to_everything: i32 = array¿5??;
. Additionally a lot of people speak spanish so they already have access to the inverted question mark on their keyboard. everyone else will just have to copy paste until the keyboard layouts catch up.