[idea] foo !: Bar - type conversion operator


#1

I’d like an operator for type conversions:

let s = "foo" !: String;
let x = 16u8 !: u32;
let y = Wrapping(256u16) !: u8;
// etc

We already have From and Into, this really just leverages those.

(also apparently topic titles can’t start with lowercase)


#2

The as operator already does this, albeit using a different mechanism.


#3

The as operator is going through a deprecation attempt. It also can’t turn &str into String.


#4

What advantage would this have over just using from and into? Those two traits and functions can already do what they can do without any additional language support and special casing. (No, “It’s shorter” in itself is not feat nor a good reason.)


#5

While I agree that .into():Type is cumbersome, the !: syntax feel completely clunky to me. It looks like a negation or a macro, and it is clearly none of them.

I think the as operator is the right way to do the job, it should just be fixed instead of just deprecated. It could be modified to use the conversion traits. For backward compatibility, when there is no matching conversion trait but a currently legal conversion, it would raise a warning (and maybe and error in a future edition).


#6

It being an operator is the advantage, so you can explain it/pass it off as the replacement for as. Technically, I propose it as a pseudo-operator: the operators foo! for into and bar: Baz for type ascription, but the compiler can easily enforce foo! to only be used in type ascriptions.


#7

So then the only difference is that foo.into(): Baz uses a few more characters?


#8

foo.into():Etc is not an “type conversion operator”, it’s also clunky and unergonomic and doesn’t feel right in common usage. it feels like something that should be avoided and not used.

foo !: Baz otoh feels like something you should use regularly.


#9

I feel differently. Perhaps we should focus on the impact of syntax on our programs, rather than on our feelings? With a little open-mindedness, the human mind can always feel differently.


#10

I do believe code is written by humans still, so how humans feel seems like an important aspect to me.


#11

I didn’t say it wasn’t important, I said it wasn’t productive. You assert that it is better, while I assert that it is worse. I don’t see any reason why your feelings on this matter should outweigh mine. So lets leave feelings off the table, ok? Let’s optimize for something we can measure.


#12

Add a nearly-random word at the beginning of your title :slight_smile:


#13

I strongly disagree. ergonomics are good for the language. it doesn’t feel like you’re fighting the language/stdlib/etc.

Imagine having to use:

let x = 16u8.into(): u32;
let y = Wrapping(256u16).into(): u8;

IMO, these feel like you’re fighting the language, unlike the current as operator. If we want to deprecate as, we should also improve the ergonomics of into. (alternatively: make yourself use these instead of as in FFI code. let me know how it goes.)


#14

Those feel fine to me. The look good, they read well, they’re unambiguous. I know what exactly functions are being called. It feels like you’re using the language.

If we want to deprecate as , we should also improve the ergonomics of into .

I don’t think into has an ergonomics problem. It’s a simple method call – If that constitutes an ergonomics problem, then the whole language is borked.

make yourself use these instead of as in FFI code. let me know how it goes.

I can imagine billions of different ways to write FFI code. Perhaps you have some particularly unreasonable code on your mind? All of the FFI code I’ve written involves doing conversions up front and then passing arguments to extern functions. And there, this would make no difference, as I’m not computing anything with long multi-typed maths expressions. In either case, I would put just values in temporaries, because as isn’t particularly pleasant either.

But that’s all just a builtin hazard of doing anything that requires careful numerical conversions. You hide that stuff behind a function and forget about the implementation. Especially for FFI code.


#15

Let’s not turn Rust into Brainfuck or Forth or (shudder) Perl. Having a bunch of Random operators when function calls suffice is not improvement. If the language adopts every pet feature someone has it will be mess 100 times worse than the illegitimate offspring of a 4-way between C++, Perl, Brainfuck, and Forth with a Lisp swizzle of ((((())))))) on top!


#16

I’d argue about centralized programming languages being a problem but hmm wait maybe I just had a better idea


#17

Beyond what @skysch has written (re. .into() being fine…) I’d also like to ask: why ! ?

Why was this symbol used and what about it screams “into”?

With ? I can see a rationale for saying let foo = bar?; as “let foo equal bar, maybe?” (or reworded: “maybe let foo equal bar”) so it works sort of naturally.


#18

Because of

fn foo<F: FnMut() -> Option<i32>>(f: F) -> Option<i32> {
  let x = get_i32()?;
  let y = mangle(x);
  while let Some(v) = f() {
    if v == y { return x!; }
  }
  y!
}

IMO ! is similar to ?


#19

I can image a reason: !: means infallable conversion Into, and ?: can be introduced later for fallable conversion try_into.

let i = function_returns_u32() ?: u8; //i: Result<u8, ConversionError>

#20

But ?: already has a meaning in the context of type ascription and Try (which I still believe should be called Propagate).