It would be nice to have syntax sugar for .into().
let s: String = "hello world"!;
Other examples: (edited because previous example didn't actually make sense)
// assuming Result<T, E> implemented From<T>
fn write_all(x: Data) -> Result<()> {
while !x.is_empty() {
write_data(&mut x)?
}
()!
}
Related: #2416
This syntax could also be used for auto-Ok() but then idk how that would work.
The proposed syntax is ambiguous with https://doc.rust-lang.org/nightly/std/ops/trait.Not.html
How?
It goes at the end. Not goes at the start.
How hard is it to disambiguate !expr and expr!?
Oh sorry; I brainfarted.
Consider:
fn weird() -> Option<Option<Option<()>>> {
().into().into().into()
}
This results in:
error[E0619]: the type of this value must be known in this context
--> src/main.rs:6:5
|
6 | ().into().into().into()
| ^^^^^^^^^
Regarding the proposed syntax; I don't think it is very legible in communicating "into".
It's like ? but where as ? unwraps/propagates something that can fail, ! does the opposite, kinda.
But yes, I guess you're right, it would only work for one layer of into. I was getting a bit ahead of myself.
Anyway ? and ! are common in english and many other languages, and one is a question and the other is an order, kinda. They're kind of opposites. It kinda makes sense to extend that to programming.
The most ambiguity I've noticed is with macros.
Is f!(x) a macro invocation of f, or is f a wrapper for some Fn type?
But like we (kinda*) do elsewhere, we should disambiguate towards a macro invocation, and use (f!)(x) for calling it. (And either way, you need a type annotation, so it would be (f!: Fnwhatever)(x).)
(* a.b() disambiguates towards a method call. (a.b)() to call a field.)
If it is supposed to be the the "wrap" to the conditional unwrap of ?, then I think expr! should be Try::from_ok(expr), not expr.into().
The ambiguity with macro invocation sounds problematic.
into is more useful than from_ok. into and unwrap are the two main things you see in any code base. (well, either an "implicit"/inferred into or an explicit conversion, really.) ? deals with unwrap, ! deals with into.
"should be" for consistency and "should be" for ergonomics are often different.
also, on the ambiguity with macros, they need an open block of some sort (one of {[(), none of those are valid after an into without type ascription afaik. i.e. all of the following error one way or another:
x.into()();
x.into()[1];
x.into(){};
So disambiguating towards macro invokation makes (more) sense.
It would be nice to have syntax sugar for
.into().
I have to disagree. It's a trivial thing to write; unlike match-Result-and-return-vs-?, it doesn't abstract away a significant amount of work. Adding a new postfix operator (or any sort of special syntax) to the core language just for the sake of replacing one very specific method is not a good tradeoff.
There are 544 calls to .into() in rust-lang/rust, which works out to around 3.8 kB. Most or all of these could be replaced with !, bringing it down to around 544 bytes.
There are 729 calls to ::from() in rust-lang/rust. I expect many of them could be replaced with .into(), and then !.
It's seemingly not a lot, but keep in mind I didn't look for any "explicit" conversions like Some(T) (not all Some(T) can be replaced with T.into() (and thus T!), but many can, and each would save around 5 bytes).
(Ofc, some projects, such as diesel, do provide their own conversion traits, so this is somewhat biased.)
If you use .into() a lot, perhaps because you love newtypes and so you use them everywhere (am I not supposed to do this?), then ! helps. (For example, I have Time32 and Time64 newtypes, wrapping i32 and i64 respectively. I use .into() to stick them into network protocols and (sometimes) databases.)
which works out to around 3.8 kB. Most or all of these could be replaced with !, bringing it down to around 544 bytes.
Ugh… Shaving off 3.3 kB in a rustc-sized crate is the argument? No, thanks, I don't buy that. It just doesn't justify either the mental burden of having to memorize yet another sigil, or the core language change. (Conversely, in a huge code base which does a lot of things and needs to be fast, I don't want to have conversions go under the radar, being hidden by magic punctuation.)
Writing code is never the bottleneck; we read code a lot more than we spend time hammering the keyboard. Terseness doesn't equal readability, and shaving off a couple of characters in itself is rarely a feat or goal.
I didn't feel like downloading every crate I could find to check. Also, many crates provide (and/or use) "explicit" conversions over "implicit" ones, so it would be biased either way. Do you have a better suggestion?
(I for example used to avoid .into() as much as possible until I started using Cow. anyway the true potential of ! wouldn't be realized until we got From<T> for Result<T, E>. maybe I should work on that first.)
Do you have a better suggestion?
Yes, we should leave poor .into() alone, use it as a method call, and not add more special syntax for individual methods.
so uh what about removing auto-Ok()-wrapping from try blocks and having ! be used for Ok()-wrapping? it's more inline with "opposite of ?" (altho I still think every case where .into() and from_ok are available, they should do the same thing, e.g. Option - maybe from_ok should be deprecated in favor of From/Into)
this also fixes the consistency issues between try blocks and functions, and doesn't require introducing try fns, keeping the language simpler and newcomers happier.
That has all the same issues: it's a niche special case for a construct which was already simple in the first place.
Ok, let's see. How about:
remove + (and all the other operators), you can use x.add(&y) instead.
remove &&, you can use if if x { y } else { false } { etc } instead.
remove ||, you can use if if x { true } else { y } { etc } instead.
these are simple constructs anyway, we don't need special operators because they're so simple.
So like, I've used Cow before. Actually some of my code uses Cow a lot. There are 50 occurrences of .into, in just one file. Having to type .into() is tiring, I know because I had to do it, 50 times. We have these operators because typing those things is tiring when you need to do them over and over.
Since my personal agenda with Rust is to encourage usage of into(), I would really appreciate to have ! for into().
The operators you brought up are widely recognized though, unlike ! which doesn't have a universally-accepted meaning. I also didn't propose we remove them or any existing operator – I merely suggested that we shouldn't add more. (Furthermore, && and || are much more convoluted in the style you have rewritten them, so "they would be simple too" is not a valid argument against them).
Since my personal agenda with Rust is to encourage usage of
into()
I use and encourage the use of into() all the time, too. But again, because it potentially does something expensive, I'd pretty much like it to be something well-visible and clear, which ! is neither.
So please don't add Ok-wrapping or throw, because they look cheaper than they are.
For the record, I don't support Ok-wrapping or throw either, for the same reason: they are already trivially realized using existing language features.
for the record, a lot of ppl are aggressively for Ok-wrapping, and it seems straight-up impossible to argue with them.
so I'm trying to come up with a compromise: add something "invisible and unclear", aka !, that is still just as explicit as an explicit Ok. It can then be linted against. (even tho I'd personally use it a lot, because I'm weird like that. I'm also the person who's fine with labeled breaks being linted against but would still love to use it everywhere.)
@SoniEx2, you mention in your code base that you have 50 or so occurences of into?
I am curious as to why you have so many cases of into?
I use into for converting to/from newtypes.
if you are converting between newtype and old type there you arent getting the benefits of the newtype.
my understanding, is there are only two reasons to create a newtype, to make type, that stricter than the original type, for example create a newtype that says number will be less 50. the only way to construct such type should through, a constructor function, that returns result
returns results.
the other use case is to apply trait to type that cant be implemented directly on type because of coherence rules.
I use into for converting from newtypes because last time I checked diesel didn't like newtypes (or at least I had issues with them).
I'm going to close this because I just don't see this happening (without a _massive_ amount of evidence, given the bar to introduce an operator). And it's not obvious that it's unambiguous given macros' use of ! anyway.
Most helpful comment
I have to disagree. It's a trivial thing to write; unlike
match-Result-and-return-vs-?, it doesn't abstract away a significant amount of work. Adding a new postfix operator (or any sort of special syntax) to the core language just for the sake of replacing one very specific method is not a good tradeoff.