Skip to content

Instantly share code, notes, and snippets.

@strake
Last active January 20, 2018 07:12
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save strake/07d80cf9db9e393a4d7016aa72f5ece3 to your computer and use it in GitHub Desktop.
Save strake/07d80cf9db9e393a4d7016aa72f5ece3 to your computer and use it in GitHub Desktop.

Rust 2018

To begin, let me make this clear: I like Rust. It's a very useful tool, despite the following complaints, and the only one i use for serious systems programming any more. (I was making my own language, but really like having some nice other people do all that hard work for me, so thank you, Rust devs!)

That said, it could be better.

The Standard Library

The standard library has a major flaw: it aborts on failure to allocate memory. This is unacceptable in many of my use cases [1]. If it is acceptable, i am using Haskell, and trust me: you really want to not be competing with Haskell (and its ilk). It is frustrating as it would be easy to use/wrap a fallible API infallibly [2], but not vice versa.

It would not be so bad, except for all the magic in libstd: to write an alternative means various dark rituals and incantations like feature and lang and such which are unstable and only work on nightly. lang in particular makes interop with crates using std quite difficult — if you define your own Box, you can't even link it in the same program as libstd, even if they are never in scope together!

Even absent lang items, it causes pain: see for example Vec in my containers crate, which is essentially exactly the same as a std::Vec — pointer, length, capacity — but is a separate uninteroperable type.

It is also mildly irritating to have to write #![no_std] in so many crates, which becomes noise if one sees it so often — in my opinion this ought to be specified in the cargo file.

Richer-Kinded Quantification

In Haskell we can define some very useful typeclasses [3][4]:

class Mappable f where
    map :: (a -> b) -> f a -> f b

class Mappable f => Bindable f where
    bind :: (a -> f b) -> f a -> f b
    bind f = join . map f

Let's consider what these might be in pseudo-Rust:

trait Mappable {
    fn map<A, F: Fn(A) -> B>(self<A>, f: F) -> Self<B>;
}

trait Bindable: Mappable {
    fn bind<A, B, F: Fn(A) -> Self<B>>(self<A>, f: F) -> Self<B>;
}

Now let's consider some impls:

impl Mappable for Option {
    fn map<A, F: Fn(A) -> B>(self<A>, f: F) -> Self<B> { ... }
}

impl<E> Mappable for Result<_, E> {
    fn map<A, F: Fn(A) -> B>(self<A>, f: F) -> Self<B> { ... }
}

impl Bindable for Option {
    fn bind<A, F: Fn(A) -> Self<B>>(self<A>, f: F) -> Self<B> { ... }
}

impl<E> Bindable for Result<_, E> {
    fn bind<A, F: Fn(A) -> Self<B>>(self<A>, f: F) -> Self<B> { ... }
}

Oh wait, we already have these methods! (bind is called and_then.) But they're not a trait method, so we can't quantify over them, alas.


Say we want to define a recursive data type:

enum List<A> { Nil, Cons(A, List<A>) }

What happens:

error[E0072]: recursive type List has infinite size

Oops, we need an indirection! Well, say we're not sure what kind of pointer we want — in some situations we want an owned pointer to a dynamically-allocated value, in others we want an atomic (e.g. for a lock-free list), whatever. This would be another use case of higher-kinded quantification, again in pseudo-Rust:

enum List<A, Ptr> { Nil, Cons(A, Ptr<List<A>>) }

Last, i wish to note the utility of data kinds (a.k.a. const generics). Look at the linea crate: we must write such hellish type signatures merely to parametrize our code over the length of an array. However, it seems const generics are in the works, so i shall say no more here.

Self-referential types

Say we have some resource A, and we want to compute another resource B which keeps a reference to A, and keep them together in a struct for convenience.

We can't.

At least, not in safe Rust.

I defer to tomaka for a lengthier explanation of the difficulties this poses.

I merely wish to note this bites me also, and really hurts if one is trying to do as little dynamic allocation as possible, as in much systems programming...

Inline Assembly

When doing systems programming, sometimes we must drop down to assembly. C has facilities to do so; stable rust has none. Potential reasons to want asm include the following:

  • Doing a system call and not spilling my locals or using the mentally-retarded errno C API
  • Writing an operating system kernel
  • Using CPU- or arch-specific performance-boosting instructions

As it is, the inline asm syntax is quite ad-hoc and baffling. This is not due to Rust — it was merely copying what came before. Nonetheless, a smoother (and preferably stable) syntax would be quite welcome. Rust has an opportunity to lead here!

Footnotes

[1] I want this for a few reasons:

  • I often write code which must continue to operate in an out-of-memory situation, to potentially allow user intervention and system recovery. I disable overcommit to stop the kernel killing some random process and destroying state -- sometimes the process using all the memory is using it for a good reason.
  • I write libraries which not presume to know better than the user how they want their program to behave in an out-of-memory situation, so any libraries they depend on must not so presume either.

[2] I write "fallible" to mean the caller can observe a failed allocation — dynamic allocation is ever fallible, but if it aborts the process on failure the caller can not observe it. I believe this is the customary use of these words in the Rust community.

[3] A typeclass is roughly analogous to a trait.

[4] These in the base library are actually called Functor and Monad [5], which is mathematical jargon irrelevant to this document. (They also have an intermediate class Applicative which is useful in its own right, but not significant to this document.)

[5] Yes, you in the front in the Haskell shirt, Monad does have another method. I omit it here for simplicity.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment