The `as` keyword is well-suited for use with postfix operator syntax

The as keyword was part of Rust’s early syntax, a time before postfix operators like .await were introduced. Consider the following example using as:

let index = y as usize * width.get() as usize + x as usize;

Issues:

  1. The as keyword’s precedence is mixed with other operators, making the expression less clear.
  2. as does not support chained operations.

Idea:

Introduce a postfix operator for as, such as .as(). Then the above expression could be written as:

let index = y.as(usize) * width.get().as(usize) + x.as(usize);

This approach makes the code much clearer.

Alternatively, considering that generics typically use <>, we could introduce an .as<> syntax. However, this would lead to the "turbo fish" problem:

let index = y.as::<usize> * width.get().as::<usize> + x.as::<usize>;
9 Likes

I find myself missing this.

The first time I encountered it, I thought "oh, well that's fine, I'll just call .into()":

error[E0277]: the trait bound `u8: From<i32>` is not satisfied

Use .try_into() for fallible conversions like this.

1 Like

(off-topic) For information there is an open issue to add a lint to suggest using try_into().

3 Likes

Regardless of whether it's into or try_into, their method implementations are non-generic. This means that when the compiler cannot automatically infer the return type, you need to use an intermediate variable to explicitly specify it. This approach is not very user-friendly.

1 Like

I usually prefer using the conv metho provided by the tap crate. It's just a function that has a generic parameter T and it calls the From<Self> for T impl. I think that's how the Into trait should have been implemented, but I guess it's too late now...

Also, I'm highly in favour of a postfix as! I don't think turbofish syntax is a problem, it's a unique syntax of Rust that solves a very common parser issue (generics or lesser than), and it also has a sad history I don't think we should look over

That said, since it would be a keyword postfix, I think it would be possible to have a parser rule to allow the syntax to be x.as<T>

3 Likes

If this were accepted, it couldn't be called as. Since that's a keyword, it would mean everyone would end up writing .r#as() (if that's even valid today) rather than .as().

I think a better plan is to have more specific functions that replace the various meanings of as, such as:

let a: u16 = 257;
let b: u8 = a.wrap();
assert_eq!(b, 1);
9 Likes

That woulds definitely help pick apart its subtleties, and make the various behaviors more explicit.

How about removing the rule that an as expression can't be followed by a dot? There is nothing preventing us from allowing

foo.bar()
    .baz() as usize
    .quux()

The precedence rules for as are actually almost the same as for the dot; the only difference is that its precedence is lower than prefix operators, so -x as i32 is the same as (-x) as i32, whereas -x.into() is the same as -(x.into()). But this is usually not a problem in practice. Actually, I consider it an advantage of as.

1 Like

This could lead to misunderstanding, as people are more likely to perceive this visually as:

(foo.bar().baz()) as (usize.quux())
1 Like

Not when the dot is in the next line. But rustfmt would have to always insert a line break between as <type> and .

Not when the dot is in the next line. But rustfmt would have to always insert a line break between as <type> and .

This clearly seems like a bad restriction for a syntax, having to require a line break

2 Likes

So now it's three function calls, but neither the function is an actual function, nor the argument is an expression. This is confusing at best.

2 Likes