Neurodiversity and Rust

programming neurodiversity rust accessibility

To say that Rust is my favourite programming language would be an understatement. Certainly, plenty of people agree with me, given it’s continued top rankings of preference in developer surveys for the past several years (even if some of it might be hype).

It has it’s flaws of course - I could talk about my strong preference for copyleft licenses like GPLv3 in contrast to the general trend of MIT/Apache licenses in the wider Rust community, or the fact that Rust is infested with scummy cryptocurrency companies, or some more minor things like overreliance on GitHub for collaboration.

However, on the whole, I love the language for it’s ergonomics, performance, safety, and package management, and the communities for their inclusivity that remains relatively uninfluenced by more corporate-style “inclusivity” that does little in the face of an entrenched culture bound in the aesthetics and trappings of the style and politics of wealthier corporate old-boy’s clubs (as you see in the big C++ and .NET organisations and conferences).

This inclusivity is most often exemplified by the codes of conduct common in Rust forums, projects, and communities, and the fact that a diverse number of people are legitimately comfortable enough to take part openly in the Rust community. However, I want to talk about something slightly different - how the language itself ends up being more accessible and inclusive to neurodivergent people because of how it is designed.

Introduction to Neurodivergence

The idea behind neurodivergence and neurodiversity is that when a person’s brain works differently to what is typical, that it is not entirely bad, but instead can be neutral or occasionally beneficial in some ways depending on circumstances and social structure. At the very least, it is the acknowledgement that perspectives from neurodiverse people are valuable and that we are not less deserving of autonomy and agency than neurotypical people.

Oftentimes, neurodiversity as a concept is attached strongly to Autistic Self-Advocacy and similar movements that act in opposition to the extremely dehumanising and infantilising ways in which autistic and other neurodivergent people are treated both now and even more so in the past. The usual two primary things referred to by “neurodivergence” are autism and ADHD (not exclusively of course and there are plenty of others that come under the umbrella even if less widely known), though ultimately this is up to the people in question on an individual basis as to whether they view themselves with this lens.

🗒 NOTE
This is not a rejection of the idea of these as being a disability, but an acknowledgement that our perspectives have value and that a lot of issues we face can be social in nature rather than some intrinsic deficiency - not all of them, but some of them - and that they can also sometimes be neutral or advantageous both individually and to groups we are part of given the right situation and social structures rather than exclusively seen as strictly disabling, or worse, as some kind of personal flaw to be entirely eradicated.

How is this Relevant?

I myself am autistic, and I have ADHD - the latter is more important in this discussion. Each of these neurodivergencies has significant effects on the way you think and what things you struggle and succeed with compared to neurotypical people. In particular, ADHD involves worse working memory, forgetfulness, and executive function (organisation and coordination and task switching and some more) a lot of the time - I’m not going to discuss the more neutral or sometimes positive aspects of ADHD in this article, as they are less relevant for this discussion.

These impact ability and stress involved in programming in many ways.

Rust’s Accessibility

Rust has a number of traits that make it more accessible to ADHDers than other programming languages, and which I would argue make it easier for everyone to program but they provide extra benefit to those of us with ADHD.

These are intentional design decisions made by the language community and team that also serve to improve accessibility, even though I don’t think it was a particular consideration in this case.

Locality of Information

One of the major principles of Rust is the concept of locality. That is, when you define a struct, function, trait, or similar, nearly all the dependencies, types, traits, etc. involved are explicit and imported, in such a way that just reading the definition of a function is nearly always guarunteed not to mess around with global state or even local state unless explicitly specified as such.

Every aspect of the language makes it easier to strictly control what state is modified - if any - and makes it much more difficult to modify state from more far reaching places, like in global variables or similar such things.

Furthermore, validation of traits and types and lifetimes is function-local and the use of them is explicit rather than implicit - you must import a trait to be able to use it’s methods, and if there’s a conflict between methods on two traits, or between a trait and an inherent (struct) impl method, you have to explicitly specify. This means that changes in interfaces far away in the program can’t break everything miles away so easily, because it will cause errors.

Rust code is local and explicit about types, traits, mutability, and also more general things like documentation. It also is implicit where this explicitness would just be boilerplate and errorprone - for example, when using allocating structures like Vec. That is, the locality of function and type definitions and their validation, and the explicitness of trait usage and namespace imports, allows implicitness in the places where being explicit is prone to errors, such as allocation and deallocation.

In my experience, compared to languages like C and C++, this is a serious benefit and means that Rust is much less stressful to write, because the amount of implicit state access and potential cross-program interference is much lower to nonexistent, which is significantly less stressful on your working memory. As an ADHDer, this has made programming far far more accessible and less stressful than the times where I’ve had to use those other two.

When your assumptions are explicit, you don’t need to hold them in your head, and that is a benefit to lots of people but most especially to ADHDers, as having to keep in mind lots of implicit assumptions across the program and state - and sync it up with documentation when necessary - is difficult, stressful, and errorprone. For me, this problem actually made coding larger programs in C nearly impossible, having to keep all the manual memory management in mind, and it made C++ difficult.

Low Boilerplate

While for many programmers boilerplate code is a minor annoyance, for ADHD people it can be a serious obstacle.

This is because one of the things ADHD people often struggle with is reading large chunks of information, as well as following very strict but repetitive rules without error (because it is easy to get more distracted or forget a step).

Filtering the relevant from the irrelevant can be a challenge, and too much boilerplatey information makes it harder for ADHD folks to keep track of where we are in a peice of text.

Syntax highlighting and name highlighting can help with this of course, but lots of nesting, extranuous annotation on functions, etc. can actually be a fairly big obstacle because all of that can be difficult to filter through when it gets bad enough. It hinders the comprehension of the actual problem space, and adds more errorprone annotations when writing out the structure of the problem space.

Rust does have long, sometimes unpleasant generic types. However, most of those actually encode something relevant to the structure of the problem and assumptions associated with a value, and in the case where they are truly extranuous, you can usually provide an impl Trait parameter, or a generic type parameter that implements traits encoding the relevant problem space instead.

There are still some limitations on returning types in that way when defining and implementing traits, though that problem is coming close to being solved at this point.

Strong Type System

Rust has a pretty strong type system. With enum, tuples, and other algebraic typesystem hallmarks like Result<T, E> and Option<T>, Rust allows you to encode problems and assumptions into the typesystem at compile time.

While not quite as strong as a language like Haskell, it is still extremely powerful. Furthermore, it has a good system for specifying shared patterns of behaviour in the form of traits, along with powerful methods of generically implementing traits on types when those types meet certain criteria, which allows the ability to nest generic type parameters to encode complex information about the nature of problems and composing types in a modular, generic fashion to plug and play various implementations to build custom structures, while still checking it all at compiletime.

That last part is important. Because Rust encodes such a large amount of information at compile time via it’s type system, it helps strongly in avoiding “silly errors” or forgetting to update some peice of code when another changes, or forgetting to handle an error case. Furthermore, this enforcement at compile time helps to avoid writing such assumptions in documentation where it can be very prone to becoming out of date.

Both administrative tasks like keeping documentation up to date, and “silly errors” like forgetting to handle a special case, are things that are even harder for a lot of ADHDers. Having the compiler check these ahead of time makes programming much less stressful (or at least it does for me, and I wager that it is not even close to a unique experience), than trying to track down a deeply nested error at runtime.

At least for me, I find the task-switch between runtime debugging and writing code more difficult than having compiler errors right in my Vim. This goes back to the concept of locality, as well.

Personal Experiences

Over my time and experience with programming, I seem to have moved more towards stronger and stronger usage of typesystems to encode the structure of a problem, as well as the use of functional programming techniques, as I’ve found these types of systems to enable correctness by construction that suits my own thoughtprocesses much better than messing around with runtime error handling and tracking down those issues.

In the past, this has always conflicted with my strong desire for more performant programs and the clunkiness of type systems in languages like C++. However, Rust appears to have successfully crossed a strong type system with performance and ease of packaging, distribution, and similar. It is essentially what I wished C++ was while I still wrote C++. For me, inefficiency bothers me for any library code I might write or use, and this was a constant source of friction when writing C++, and often in C++ I found that typesystem encoding of facts and structure was worse-performing and unergonomic because of the lack of inbuilt algebraic typesystem primitives.

Rust still isn’t perfect in certain aspects of type system capability - for example, it still lacks variadic generics (for being generic over arbitrary-length sequences of types) - however the recent stabilisation of Generic Associated Types and many other features dependent on it should finally enable even stronger encoding of problems in the type system.

Great Tooling

Rust has an amazing suite of tools. More importantly, it’s tooling that makes administrative tasks simple. New projects, publishing on crates.io, formatting code automatically, obtaining dependencies in such a way that they can be used easily.

Being able to perform these administrative tasks in a simple and consistent way is in stark contrast to the extreme difficulty involved in managing dependencies, creating self-contained packages, and similar tasks, using the tools for other languages like C++, C, Python, and others. Each of those has it’s own clusterfuck of mechanisms to depend on other things, or list out files to build where you have to constantly keep it maintained (e.g. in CMake you have traditionally had to list out every .c/.cpp file to build, manually).

Because task switching and administrative tasks are typically much more difficult for people with executive function problems (I in particular am Autistic and have ADHD so it’s multifactor, but it seems to be common), having tools that can perform most of the repetitive parts of these tasks in a declarative and automated manner is a benefit - especially in contrast to having to manually do lots and lots of tiny maintainance tasks that can cause difficulty when programming due to the cost of switching between different types of tasks on-demand for a lot of neurodiverse people.

This also applies to documentation. Most other languages have a whole other separate system to create documentation and accurately link different parts together, which must be managed and set up separately. Python does have the notion of docstrings, but all other documentation systems I’ve personally come across pale in comparison to the simplicity and lack of disruption to normal coding practice in comparison to Rust’s documentation system. Furthermore, most of these other systems require manually redocumenting things like function argument types and such that already exists in the code itself, which is even more administrative overhead that remains easy to get out of sync and forget to do.

Conclusion

Hopefully this was an interesting perspective on some aspects of Rust and it’s community, that may be minor for neurotypical programmers, but are significant help for me as a neurodiverse person, with parts of programming I have struggled with in the past around executive function. There’s other things, like the orthogonality of data structure and interfaces (as opposed to in OO with inheritance that conglomerates both of those categories together) that I also find helpful, but the main aspects have been covered.

So, for my fellow ADHD and neurodiverse-in-general programmers who have difficulty with the systems of other languages, maybe consider giving Rust a shot. For me, at least, I found it very helpful in making me more productive and less stressed, in the context of ADHD and executive dysfunction related issues with programming in other languages where I struggled to finish anything complex due to the constant administration necessary.