Std ops PanicInto / std ops Magic / std ops SwissArmyKnife

PanicInto is like Into meets unwrap.

All Into would also be PanicInto. Additionally:

Option<T> is PanicInto<T>
Result<T, E> is PanicInto<T>
&[T] is PanicInto<&[T; N]>
T is PanicInto<Result<T, E>> since y’all hate From<T> for Result<T, E>

The operator is used like:

fn foo() -> Result<(), MyError> {
let x: i32 = Some(15)!;
()!
}
let a = foo![0];

Is this a macro call, or an object conversion followed by an index operation?

1 Like

We disambiguate towards macro calls, because (foo!)[] (foo!)() (foo!){} wouldn’t compile.

foo!:Bar[] would disambiguate to Magic, because the type ascription makes it compile.

it’s actually quite logical. there’s no conflict with macro syntax no matter how much you want there to be.

macro names never end an expression. Magic always ends an expression.

? started as try!. Give us bang! or unwrap! or whatever to experiment with, then there can be fruitful discussion.

2 Likes

We disambiguate towards macro calls, because (foo!)[] (foo!)() (foo!){} wouldn’t compile.

That's the first I've heard of this. And I've already explained to you why a syntactic ambiguity is a problem regardless of whether the compiler can solve it. Did you just forget the previous discussion?

I don't know why you are always expecting me to anticipate syntax rules that you never actually state. And now, having stated them, it's not clear how you plan to enforce them. You say it won't compile, but you don't say why it won't compile. Apparently type ascription enables the syntax? And so does returning from a function? But not simply using it in any other expression?

EDIT: Ah, I see what is going on. The problem is that you can't infer that something is 'indexable' or 'callable', so there's no way to convert into it anyway. I apologize for my tone.

you mean uh, impl Into<T> for Option<T>?

In a crate, implement the suggested trait and the sugar as b!(foo) instead of foo!. It’s how try started, as try!(foo) which graduated to foo? when it was seen as core enough to deserve the dedicated syntax.

This is a simple enough addition that you could write the crate in a day. Give us something to actually experiment with! (If you’re feeling ambitious, make a attribute proc_macro to transform a fn using foo! into one using b!(foo).)

3 Likes

more specifically: you can’t infer anything after an !, so it’s an “stopper” as far as the expression is concerned. whereas macros are “starters”.

we’d even be able to have foo!.bar macro syntax and not conflict with Magic!

(ofc, the compiler’s pattern matching doesn’t necessarily match our expectations of patterns… just because it can’t be ambiguous, doesn’t mean it can’t look ambiguous. but generally speaking you can tell foo!; and foo!(); etc apart - these aren’t visually ambiguous.)

basically, currently rust has “starters” and “middlers” (if I’m not missing anything). for starters:

ident, macro!(invokation), (tuple), [array]

for middlers:

[index], (call), .field_or_method, etc

(note that () and [] show up on both starters and middlers!)

and “stoppers” generally can’t be followed by either starters or middlers (except ; but that one is very special, so I don’t consider it a stopper.), whereas macro invokation requires a specific kind of middler (specifically, one of (), [] or {}) to validate.

(this is extremely difficult to explain in plain english and I’ve gone through like 5 or so iterations of this “starters middlers and enders” idea… then again, I don’t exactly write compilers/parsers for a living…)

(also, a nice thing of Magic is that, specifically for integer types, 1u64 !: usize produces the exact same results on both 32-bit and 64-bit platforms! as for <large number>u64 !: usize on 32-bit platforms, it just panics! this is different from TryFrom/TryInto in that you don’t get different types on 32-bit vs 64-bit! thus, as far as I can tell, Magic isn’t blocked on any of the issues currently blocking TryFrom/TryInto.)

Even if it’s technically unambiguous from the parser’s point of view, I’d rather spend my time on actually meaningful details of the code, instead of wondering (and possibly getting wrong) if some niche sugar is doing one thing or another.

Oh it’s not meant to be niche, it’s meant to replace as and provide ergonomics around Result<T, E> and Option<T>, such that you never write Ok(…), Some(…), result.unwrap(), or option.unwrap().

It also provides "string literal" !: String.

When I say “swiss army knife”, I mean it. The idea is to use this all the time.

I thought the point of unwrap was that you shouldn’t use it all the time.

3 Likes

You should when you know what you’re doing, for example if you guard your whole function/method with something that guarantees initialization, or if you’re using Option for move-on-drop.

This topic was automatically closed 90 days after the last reply. New replies are no longer allowed.