Hacker News new | ask | show | jobs
by killercup 2915 days ago
> Who determines the output type of "a + b"? "a"? "b"? Both? Are there implicit type conversions?

The Add trait is defined in `std::ops::Add` as:

    pub trait Add<RHS = Self> {
        type Output;
        fn add(self, rhs: RHS) -> Self::Output;
    }
(You can find the documentation on <https://doc.rust-lang.org/1.26.2/std/ops/trait.Add.html>)

Both the type of the `self` parameter and the concrete type of `RHS` determines which trait impl matches. For example: `2 + 2` chooses the `impl Add<i32> for i32 { type Output = i32; ... }` implementation (literal integers are of type `i32`). Something like `2 + &2` would choose `impl<'a> Add<&'a i32> for i32`.

> There's nothing here about what Rust does about conversions, but that may be covered in one of the other many parts of this series.

With operators, there are no implicit conversions involved, AFAIK. So all Rust can do is try and infer the type you want. For literal numbers, it can do so in a limited fashion. As seen above, if you write `let x = 42;` the type is inferred as `i32`. If you write `foo(666)` with `fn foo(x: u64)`, the literal 666 is inferred to be meant as `666u64`.

> The good news is that the Rust programming book is finally coming out in just 6 more days. So there's something better to read than this.

You can already read it here: https://doc.rust-lang.org/book/second-edition/index.html :)

3 comments

To elucidate on this: Rust’s trait implementation coherence rules ensure structurally that there is never any ambiguity to be resolved. If I `impl<T: MyTrait> Add<T> for MyType`, I am then barred from writing `impl Add<AnotherType> for MyType` if AnotherType implements MyTrait, as that would cause a conflict, but I am permitted to if it does not, because there is then no conflict. Also you can’t implement any form of Add for MyType from your crate, because you can only implement traits from your own crate on any type, or traits from any trait on your own type, not traits from other crates for types from other crates.

(I’m still simplifying this a little, because specialisation is a thing which does allow overlapping implementations, but it must be opted into by the broad implementation by writing `default fn` instead of `fn`.)

Actually you can do that:

http://play.rust-lang.org/?gist=566c7ddb5ec90836004a2549aa78...

Because `impl Add<Bar>` and `impl Add<Baz>` are two clearly different impls there is no ambiguity (RHS is known at compile time).

What specialization would solve is this kind of ambiguity:

http://play.rust-lang.org/?gist=34142671953f75f0448b03bb804f...

Because `impl<T: Debug> Add<T>` and `impl Add<Baz>` are conflicting, since Baz is `:Debug` so both impls apply for a Baz RHS.

> With operators, there are no implicit conversions involved, AFAIK.

Beyond autoref and autoderef, anyway.

> if you write `let x = 42;` the type is inferred as `i32`.

It can be influenced by the x's usage.

    let x = 42;
    let y = x/2 + 21u32;
x is u32 here.
Yes the rule is that it defaults to i32 only if there's absolutely no way to infer the exact type by use like in your example. So in practice it almost never happens because as soon as you use the value in an operation or function call the compiler knows the exact type. One exception would be code like:

    fn main() {
        for i in 0..10 {
            println!("{}", i);
        }
    }
Here there's no way for the compiler to know what exact integer type is expected so it defaults to i32. There was a debate about this, I was personally in opposition but in my experience it mostly occurs on small "hello world" examples and code snippets so it doesn't really matter. I've never encountered any issues stemming from this rule so far. At least they got rid of the "int" and "uint" types which is a great thing in my opinion.
GP is talking about the default value for "unconstrained" literals, yours is constrained by the second expression: assuming just the stdlib,

* Add<u32> is implemented for u32 and &u32

* thus x/2: (u32 | &u32)

* there's no Div<Output=&u32> so x/2: u32

* Div<Output=u32> only is only "Div<u32> for u32"

* thus x: u32

Interesting... I'm a newbie at Rust, and expected y to be a float, because of the division.

But it turns out

    let x = 11;
    let y = 11/2;
==> y = 5 Given how type safety is important to Rust, I expected this to panic rather than do an automatic cast.
There is no type safety issue, and there is no cast. Div on integral types is explicitly implementing as an integer/truncating division: https://doc.rust-lang.org/src/core/ops/arith.rs.html#436-452

There would be no type-safety issue or cast if it were implemented as a real division either, incidentally.

There is room to argue Div should not have been implemented at all on integral types (as in Haskell where integer division is a separate operation entirely), but that's a completely different issue.