News for hackers. Stuff that matters.
Re:Monads and GATs in Nightly Rust by Animats on Friday December 11, 2020 @02:15PM (#25389516)
I kind of get that feeling, too.

Rust uses "traits" to do both generic-like things and inheritance-like things. It's not clear this was a win. The result seems to be a system which does both badly.

Rust generics are very restrictive. They're not at all like C++ generics. It's not enough that every operation on a type needed by a generic be implemented. The type has to be part of a single trait that provides for all those operations. It's like C++ subclassing. So generics over types defined by others can be impossible to write in Rust. This has no safety benefit, since generics are resolved at compile time and all safety tests can be made after generic expansion.

Traits and fixed array bounds do not play well together. This is considered a bug, and it's been a bug since at least 2017. Generic parameters can only be types, not numbers. This led to a horrible hack involving a Rust crate which has types U1, U2... U50 or so, so small numeric constants can be bashed through the Rust type system.

Not seeing the benefit of all this.

I have to go struggle with another non-helpful "lifetime `'static` required" message from the borrow checker now.

Re:Monads and GATs in Nightly Rust by Nullabillity on Friday December 11, 2020 @02:32PM (#25389760)
> This has no safety benefit, since generics are resolved at compile time and all safety tests can be made after generic expansion.

The compiler can verify that types are satisfied. It cannot verify that you agree on what those types and operations mean.

For example, `Iterator` isn't very useful without being able to agree on what `Iterator::next`'s `Option` means. So implicit structural traits á la Go is somewhere between useless and actively harmful.

You can have nominal traits while allowing orphan instances (that is, implementations of foreign traits on foreign types). Scala and Haskell both have that. But IMO neither has a good solution for the conflicts that inevitably arise in that case.

Re:Monads and GATs in Nightly Rust by pixel_fcker on Friday December 11, 2020 @02:32PM (#25389748)
const generics are very close to being done, which will fix the array issue you mention by allowing code to be generic over integers as well as types. This is already in for built-in arrays and traits but user traits currently require nightly.

I agree with you about the orphan rule being annoying and wish they’d relax that, but I much prefer traits to C++ duck-typed templates, if only because it makes error messages from passing an unsupported type to a generic function clear and concise as opposed to the thousands of lines of confusing output you often get in C++.

Re:Monads and GATs in Nightly Rust by matt_kantor on Friday December 11, 2020 @02:37PM (#25389845)
> Traits and fixed array bounds do not play well together. This is considered a bug, and it's been a bug since at least 2017. Generic parameters can only be types, not numbers.

FWIW the remedy for this is being stabilized soon: https://github.com/rust-lang/rust/pull/79135

Re:Monads and GATs in Nightly Rust by brundolf on Friday December 11, 2020 @04:36PM (#25391363)
> Rust uses "traits" to do both generic-like things and inheritance-like things. It's not clear this was a win. The result seems to be a system which does both badly.

IMO the fact that Rust doesn't do inheritance well is a feature, not a bug. My understanding is that most people even in OOP circles have realized that composition is better than inheritance, and I see Rust's choices around it as a reflection of that.

> It's not enough that every operation on a type needed by a generic be implemented. The type has to be part of a single trait that provides for all those operations.

Maybe I'm misunderstanding, but do you know that a generic can combine multiple traits? For example:

  fn foo<T: Copy + Add + Sync>(x: T) {
    ...
  }
Here T must implement Copy and Add and Sync.

Maybe what you're talking about is the fact that methods' identities aren't fully described by their names + types, but also by their trait? i.e.:

    trait TraitA {
     fn foo(&self, x: i32) -> i32;
    }
    
    trait TraitB {
     fn foo(&self, x: i32) -> i32;
    }
    
    struct Foo {
    }
    
    impl TraitA for Foo {
     fn foo(&self, x: i32) -> i32 {
      x + 1
     }
    }
    
    fn requires_b<T: TraitB>(x: T) {
        x.foo(12);
    }
    
    fn main() {
        let f = Foo { };
        requires_b(f);
    }

    error[E0277]: the trait bound `Foo: TraitB` is not satisfied
      --> src/main.rs:25:16
       |
    19 | fn requires_b<T: TraitB>(x: T) {
       |                  ------ required by this bound in `requires_b`
    ...
    25 |     requires_b(f);
       |                ^ the trait `TraitB` is not implemented for `Foo`
This was an opinionated decision made by the Rust team - which I think was a good decision - to address the fact that it's possible to have unwanted overlaps between trait method signatures. Just because fn foo(&self, x: i32) -> i32 is defined for a struct, doesn't mean it's the same foo that you're wanting to call. Distinguishing things by trait strengthens the contract. It also avoids the diamond problem. You're right that this isn't directly related to "safety" in a memory sense, but it's part of an overarching philosophy of Rust that encourages intentionality and discourages footguns.

> Generic parameters can only be types, not numbers.

This is a work in progress and has partially been rolled-out; the full implementation is on the way: https://rust-lang.github.io/rfcs/2000-const-generics.html

Re:Monads and GATs in Nightly Rust by vips7L on Saturday December 12, 2020 @09:51AM (#25398549)
The thing I found confusing with traits and their generic system was the choice to include "associative types" for out types instead of also defining them in a generic bound:

    trait Iterator {
        type Out;

        fn next() -> Option<Out>;
    }
vs

    trait Iterator<T> {
        fn next() -> Option<T>;
    }
Multiple ways to do something has made it harder to understand.
Re:Monads and GATs in Nightly Rust by zozbot234 on Saturday December 12, 2020 @09:04AM (#25398273)
> This has no safety benefit, since generics are resolved at compile time and all safety tests can be made after generic expansion.

Rust has macros and procedural macros for the "check after generic expansion" use case. It's a good thing that this is separate from generics; it side-steps a whole lot of incidental complexity that's seen in C++.

Re:Monads and GATs in Nightly Rust by lights0123 on Friday December 11, 2020 @02:37PM (#25389835)
> This has no safety benefit, since generics are resolved at compile time

It's not only safety. That's why C++ concepts were invented. https://cpp.godbolt.org/z/8Gcrj6