Most coveted Rust features

Integer type parameters. Associated constants, which are currently broken. Abstract return types aka impl Trait. Higher kinded types. Associated traits. Maybe single inheritance? Although I think the usefulness is very limited and niche.

After that, Rust would be the perfect language.

2 Likes

Slice patterns are high on my list. Very rusty feature that will be used a lot when it arrives.

3 Likes

Oh, I forgot to mention: incremental compilation.

8 Likes

Abstract return types (no active rfc), enhanced lifetime Ellison (0141), and language server (1317)

1 Like

One that hasn't been mentioned (so far as I know):

struct Window(...);

impl Window {
    pub fn do_a_thing(self: Rc<Self>) { ... }
}

Look, sometimes I just need a refcounted type. Single-type extension traits are a huge PITA, and having to wrap the type and hide the Rc (and then deal with the problems that causes like trying to hide the internal type while also making it possible to use Weak<Internal>) is also a PITA.

2 Likes

@stebalien I don't think that's right?

Parameterized modules are to modules what functions are to base
values. Just like a function returns a new value from the values of
its parameters, a parameterized module builds a new module from the
modules given as parameters. Parameterized modules are also called
functors.

This seems like an ideal place to use a trait rather than an inherent impl.

struct Window;

trait WindowTrait {
    fn do_a_thing(self);
}

impl WindowTrait for Rc<Window> {
    fn do_a_thing(self) {}
}

impl WindowTrait for Weak<Window> {
    fn do_a_thing(self) {}
}

Is there some reason that doesn't work for your use-case?

Well, that ignores the problems with both approaches and misses the point. You wouldn't implement the extension trait for Weak<_> because you have to upgrade weak references to strong ones first and that can fail.

Extension traits suck because you have to repeat all the function signatures twice, and you can't call any of the methods without importing every individual trait everywhere you use a type.

Wrapper types suck because now the user cannot use other pointer types in cases where the specific pointer type doesn't matter. As a consequence, you can't turn Rc<T> into a Weak<T>, and you can't implement direct support for the conversion because that requires you expose the internal type, so you need a new WeakRef type that also wraps Weak<T> except because they're not stable yet you can't implement unsizing coercions and on and on.

None of which would be necessary if I could just specify the Self type.

Ah. Rust can already do this with unit structs (I've actually used this pattern):

pub struct MyModule<A: SomeTrait, B: SomeOtherTrait>(PhantomData<A>, PhantomData<B>);

impl<A: SomeTrait, B: SomeOtherTrait> MyModule<A, B> {
    pub fn do_something() -> Something {
        A::do_something()
    }
}
// ...
type MyMod = MyModule<Something, SomethingElse>;

fn main() {
    MyMod::do_something();
}
1 Like

Two things:
-Incremental compilation
-Errors during compilation have links to webpages or a help manual that provide better detailed descriptions of the error

They do?

Compile this:

fn mutate(x: &mut i32) -> &mut i32 {
    *x += 1;
    x
}

fn main() {
    let mut x = 0;
    let y = &mut x;
    // use the mutable reference
    let z = mutate(y);
    // use it *again*
    mutate(y);
    println!("{}", z);  // prints "2"
}

And you get (with "see the detailed explanation for E0499" as a clickable link, playpen: Rust Playground ):


            <anon>:12:12: 12:13 error: cannot borrow `*y` as mutable more than once at a time [E0499]
<anon>:12     mutate(y);
                     ^
<anon>:12:12: 12:13 help: see the detailed explanation for E0499
<anon>:10:20: 10:21 note: previous borrow of `*y` occurs here; the mutable borrow prevents subsequent moves, borrows, or modification of `*y` until the borrow ends
<anon>:10     let z = mutate(y);
                             ^
<anon>:14:2: 14:2 note: previous borrow ends here
<anon>:6 fn main() {
...
<anon>:14 }

On the shell, you get:

error.rs:12:12: 12:13 error: cannot borrow `*y` as mutable more than once at a time [E0499]
error.rs:12     mutate(y);
                       ^
error.rs:12:12: 12:13 help: run `rustc --explain E0499` to see a detailed explanation
error.rs:10:20: 10:21 note: previous borrow of `*y` occurs here; the mutable borrow prevents subsequent moves, borrows, or modification of `*y` until the borrow ends
error.rs:10     let z = mutate(y);
                               ^
error.rs:14:2: 14:2 note: previous borrow ends here
error.rs:6 fn main() {
...
error.rs:14 }
            ^
error: aborting due to previous error

One of my most coveted features is something akin to linear types:

https://github.com/rust-lang/rfcs/issues/814

I think they would solve quite some problems very nicely and open up support for better implementation of things like session types.

https://github.com/Munksgaard/session-types

2 Likes

I think what you actually want is

impl ::std::rc::Rc<MyType> {
    fn frob(&self)  { }
}

where MyType is in the current module.

Anyway, this isn't a problem in D because their UFCS is the inverse of ours. In D, all functions can be treated as methods, while in Rust, all methods are free functions. Thus, you would only need to define a function

fn do_a_thing(val: Rc<Window>) { }

And the extension method would, you know, work.

Also, this doesn't work for your by-value method, but maybe newtyping wouldn't be so painful if you could #[derive(Deref)] for all single field structs and tuple structs. Speaking of which, why isn't there a DerefMove?

Asychronous programming construct such as stackless coroutines or async/await. Discussion and justification is here.

Could you provide some examples where we need higher-kinded types? Because as I've explained for one example as follows, "higher-kinded" seems to have become a catch all phrase for issues that ostensibly some of us don't fully understand. I had to research this to nail down an example and I am searching for more.

According to the creator of Scala, Martin Odersky, they ended up not finding any strong use cases for higher-kinded types in the libraries. Or at least that is what he had remarked some years ago.

Afaics, higher-kinded types are needed for a trait to specify the implementing class's (or for Rust struct's) type in the trait, e.g.:

trait HigherKinded<S<A>> {
   fn f<B: A>(x: B) -> S<B>;
}

But that violates the advantage of using ad hoc polymorphism in that we should only access the class S<A> via a trait type and never directly, thus instead we'd prefer:

trait NotHigherKinded<A> {
   fn f<B: A>(x: B) -> NotHigherKinded<B>;
}

Without higher-kinded types, a trait can only expose a discrete set of associated types. HKTs allow associated types to contain external lifetimes etc.

A popular case that wants this is collections:

trait Collection {
    type Iterator<'a>: Iterator;
    fn iter<'a>(&'a self) -> Self::Iterator<'a>;
}

through just allowing for non-standard supertraits may be enough:

// does not work
trait Collection where for<'a> &'a Self: IntoIterator {
    fn iter<'a>(&'a self) -> <&'a Self as IntoIterator>::IntoIter {
        <&'a Self as IntoIterator>::into_iter(self)
    }
}

And my point was/is that explicitly exposing associated (aka those implementing sub-) types is an anti-pattern, because it is not generally composable because explicit types are hard-coded cases whereas access via traits are extensible via orthogonal implementations.

Your factory method could return a trait and not the explicit type that implements the trait.

But the error (or challenges) in my conceptualization is at least that a trait doesn't inform the compiler the data size of its implementing type nor which other traits its implementing type implements.

Well the data size problem can be resolved by never storing objects unboxed on the stack nor unboxed in data structures. This has a performance cost of course.

The early binding to a specific trait could be solved by parametrizing the trait on the types of traits desired but then we need first-class intersections (aka conjunctions), e.g.:

trait Collection<T> {
   fn iter() -> Iterator & T;
}

See I am advocating a design which inverts the control of typing for maximum composability (a complete solution to Wadler's fundamental Expression Problem). This is a design I was advocating for Scala 3. I am now starting to think I have to create my own language, because there isn't anything that will do this. Ceylon has first-class conjunctions and disjunctions, but they chose the anti-pattern of subclassing instead of ad hoc polymorphism. Perhaps I could have gotten Gavin King's ear a couple of years ago, but my conceptualization wasn't yet fully formed.

I'd really love if someone had already created the language I want. I was thinking Rust might be it, but I can start to acknowledge now that the focus is more on low-level compromises (for low-level optimization) to the potential high-level leap forward I am conceptualizing (for high-level optimization).

Please do correct me or point out other perspectives. I like feedback on this idea.

Edit: appears my idea is an alternative to early binding with type families.

1 Like

We implemented associated types for a reason. In fact, there are several:

  1. Type parameter creep. Before associated types every other generic struct had to take half-a-dozen generic type parameters to be witnesses for the associated types.
  2. Associated types allow a single type to define an infinite tree of types, which is supposed to be useful sometimes. I am not aware of a practical use for this (could someone help?)

Surely you mean, return a trait object? Having iterators be boxed is simply unacceptable, given that they are supposed to desugar to for-loops.

Where does the quantifier go here? How would an impl (for, say, a Vec<T> returning a slice::Iter<'a, T>) would look like?

Correct. Apologies for not including the & for the correct Rust syntax. Note where I had written Iterator & T, I was implying & to be a hypothetical a first-class conjunction type operator (which Rust obviously does not have). Let me correct the hypothetical syntax also employ associated types (btw, which is named an abstract type in Scala):

trait Collection {
   type T;
   fn iter(&self) -> &(Iterator ∧ T);
}

That is an orthogonal performance (and language design) issue to my point about inversion of control over typing to delay premature specialization, in order to maximize composability.

A solution is use category theory Functor.map instead of iterators, which also solves the memory leak problems at a high-level. Use a more functional programming model, instead of a C/C++ imperative style model. A functional model can enable much more efficient deforestation of chained modular operations (although lazy evaluation is typically required), which can't be achieved modularly with an imperative style.

Do you mean you want an iter method that takes an input argument for the size of the slice to iterate? Or are you referring to that I forgot to include the &self argument?

Edit: note I only started learning Rust 3 or 4 days ago, haven't read all the documentation (just scanned some of it quickly), and I haven't even installed a compiler. Doing it all in my head.