Generic mutability parameters

No, all 3 are different types. For example, you may have noticed that you can impl a trait for a T, &T, and/or &mut T. You may also have noticed that &T is a Copy type, whereas &mut T is a move-only type. LLVM IR is a low level representation/mapping, which doesn’t necessarily carry the frontend language’s type system (it’s more of an abstract machine level IR, if anything).

&'b i32 can be a subtype of &'a i32 if 'b outlives 'a, so these aren’t different types necessarily. For example, &'static i32 is a subtype (and thus substitutable) for any arbitrary/generic &'a i32.

If you haven’t yet, I recommend reading nomicon’s section on variance/subtyping and how mutability plays a role (eg makes things invariant).

Yes, if you can abstract over them which is what this thread is about with regards to mutability :slight_smile:. My main point is they’re different types.

Good points.

Yes - immutable references can be copied freely and mutable not so. But if you don't wish to actually mutate the referent, you can always choose to downcast a mutable ref to immutable, meaning that mutable is a superset (subtype) of immutable, at least in some regard. Rust's syntax doesn't necessarily frame it that way so obviously, but the fact that you can assign a &mut T to a &T-binding should be some indication of this.

Thanks, I will definitely check it out. Variance is always something of a brain-twister!

Yes, if you can abstract over them

Sure - my idea is only for the implementation of functions (/traits/structs) which would abstract over an input's mutability. This means that within the function, that type would have to be treated as immutable (the base type). But the caller would benefit from maintaining full type information of its input in the returned value, instead of it being downcast to immutable.

From the way mutability is treated in Rust, they are certainly more distinct than lifetimes. I imagine this is down to the fact that there are only two values, which appear everywhere, whereas lifetimes are an infinite number of "ephemeral" types, barely worth an individual definition.

While I'm not convinced that mutability is fundamentally "more" of a type-level distinction than lifetimes, I agree that each mutability-based type (i.e. mut and imm) is more well-defined, and the two attributes cannot be treated in the same way.

Maybe I'll feel differently after reading this chapter.

This is commonly referred to as “pointer weakening” - it’s a form of coercion to make it easier to work with Rust. There’s also something called reborrowing, which makes it look like &mut T is being copied but it’s not. Again, to make it easier to write normal code.

To give an idea of why mutable isn’t superset of immutable, despite the coercion making it look like that in some way, consider these two methods:

fn get(&mut self) -> &SomeType {...}

fn get(&self) -> &SomeType {...}

The mutable version, despite returning an immutable output, “locks” self - you cannot borrow it again until that returned reference is dead. The mutability “carries over”. This can get particularly interesting if you borrow self mutably for a lifetime parameter of the type - you get a value that cannot be borrowed again, period. So (in this example) &self and &mut self behave differently at a fundamental level.

Rust has other features that make it easier and more ergonomic, such as autoref and autoderef. But those things don’t change anything fundamental.

Lifetimes get erased after borrowck runs, but & vs &mut persists. You can’t impl a trait for &'a T and &'static T - they’re not different types at this level. So although (generic) lifetimes are part of the system, they’re sort of necessary appendages to the references but it’s the references that are the types.


Yes - I should have been more clear when I said you can "choose to downcast a mutable ref to immutable". I meant as the receiver/owner of a new (or reborrowed?) reference, you can do this, regardless of the parent T/&T/&mut T. So if the source is a &mut T, then it will still be locked until the child &mut T is dead, but that child can be downcast to a &T. In fewer words:

fn get(&mut Foo) -> &mut Bar {...}
let mut foo: Foo = ...;
    let bar: &Bar = get(&mut foo); // Downcast on receive of new reference
    let baz = bar;
    println!("{} {}", baz, bar); // Ok
mutate(foo); // Unlocked

Your function signature fn get(&mut self) -> &SomeType {...} implies that this downcasting was already done somewhere inside the function.

The reason I believe this makes it a superset is because, with a &mut T, you have the choice of a) mutating/reborrowing once, or b) infinitely copying immutably (in the form of new &Ts). &T only lets you do b).

You can’t impl a trait for &'a T and &'static T

Because no lifetimes (other than 'static) exist at "design-time", this is true: lifetimes can only be mentioned in generic terms. However, I believe if a dynamic language (i.e. one where new functions are defined at runtime) had a concept of lifetimes, then they could be defined concretely within than runtime context.

All of the (two) enumerable values for mutability always exist, no matter the context. So we can mention them in concrete terms. However, I don't think this means they can't also be expressed generically.

I haven't quite finished the chapter on mutability/lifetime variance, but it's some good food for thought. I'll have to consider its consequences with respect to my proposal, especially for more complex use cases (e.g abstracting over the mutability of &[mut] &[mut] T...).

I do like intention of this topic. In terms of getting you thinking of what could be possible. My gut says it is wishful thinking. I also see it missing the Move case which often [but not always] comes with you have implementations for the other two.

Yes (in theory). nomicon aliasing probably good explanation. (The are other not so good links to aliasing too else where.)

I personally see both lifetimes and mutability as metadata; this topic make me question whether I should be mentally splitting the data (i.e. pointer) as the entire data type from the metadata. (I still don't mentally like &T as Copy / &mut T not since any second &T lifetime is not tied to the first &T)

I think this is a case of bad terminology. The function will be dereferencing to generate the reference. Code maybe explains difference best;

    let mut i = 0;
    let m = &mut i;
    let _i1:&i32 = m;
    let _i2:&i32 = m;
    //let _i3 = &i; // does not work but would if m was immutable ref.

The lifetime is not important here. What is important, like with any Copy type, is the original (source) is still live/valid/usable after a copy is taken.

Thinking about this some more, I think what @tobz1000 is proposing is not an abstraction over mutability. Not in the same sense that AsRef/Borrow and friends abstract over ownership - in their case, the user of these types truly doesn’t know whether the underlying type is borrowed or owned. But you can’t abstract over mutability because it’s a core concept and difference between the types. I’m not even sure what it would mean to abstract over mutability - either you need to mutate something or not, there’s no abstraction that I see.

Instead, what’s being proposed is closer to fn overloading and/or extensions to type inference where a single method can be used to obtain different mutability.

CC @RalfJung, who in another thread proposed having a way to abstract over "reference modifiers" (i.e. mut and, in the future, potentially pin)...

I wonder how prevalent this problem is outside of accessors? I generally have no issue with having double-accessors there.

By the way, the wanted pattern can be somewhat implemented with traits:

struct Target {
    attribute: i32

trait Attr {
    type Output;
    fn attribute(&self) -> &Self::Output;

trait AttrMut: Attr {
    fn attribute(&mut self) -> &mut Self::Output;

impl Attr for Target {
    type Output = i32;
    fn attribute(&self) -> &i32 {

impl AttrMut for Target {
    fn attribute(&mut self) -> &mut i32 {
        &mut self.attribute

fn one_way() {
    let mut target = Target { attribute: 0 };
    let x = (&mut target).attribute();
    *x = 42;
    let y = (&target).attribute(); // not okay

fn the_other() {
    let target = &mut Target { attribute: 0 };
    let x = target.attribute();
    *x = 42;
    let y : &i32 = target.attribute(); // not okay
1 Like

I was also thinking when I first read this post of a way that traits could do something similar.
The OP desire (in part*) is for code reuse so I see your code failing since you have written both bodies &self.attribute and &mut self.attribute
(* other part is not using a second _mut identifier.)

Do you mean as in the following?

fn get_move(self) -> T;

I hadn't thought about it until now, but it does make sense. If you provide an even less constrained input to such a function, you get an equivalently unconstrained output (full ownership). The resulting bytecode of the ownership variant of the function would be significantly different to the reference variant(s), but that isn't such a problem.

The main problem I see with this addition is that the generic function would be unable to take advantage of the moved variable, which might prevent some optimisations the programmer could otherwise make. For example:

struct Foo {
    bar: Bar;
    baz: Baz;

fn some_network_stuff(bar: Bar) { ... }

impl Foo {
    fn get(&self) -> &Baz {
        some_network_stuff(; // Have to clone here

    fn get_move(self) -> Baz {
        let Foo { bar, baz } = self;
        some_network_stuff(bar); // No clone necessary

In a generic form of the function, the clone would occur even when the input Foo was being moved by the caller (at least without a very clever compiler optimisation).

It depends what you're referring to as "the user". If you mean the caller of a generic function, it will know the mutability state of the input/output.

However, the generic function itself will not know the mutability and therefore cannot mutate it. It can just say "you gave me this reference; I'll give you another reference derived from it. If you could mutate it before, you can still do that now. ¯\_(ツ)_/¯". This is abstraction over mutability from the point of view of the generic function.

It's not really overloading - the two versions of the generic function would behave the same. Overloaded functions can have arbitrarily different behaviour. (Generic functions are still turned into multiple real functions for each input type at compile time of course, but the programmer cannot affect their behaviours differently.)

I meant the generic function itself. I agree with @skade that your proposal only really helps the trivial case of simple accessors. A better example of where abstracting over it would be useful is this thread - it has two versions of a method, one for each reference type, but 99% of the code is the same between the two.

Right - it’s not really overloading but is closer to it than to abstraction from the fn implementation point of view. It’s actually closer to something like Into::into() where the target type is provided by the caller (explicitly or inferred). Your case is essentially that (at a high level) except mutability of the target is selected by caller.

As mentioned, the utility of this is very marginal if the fn itself cannot differ. What you’d really want is a way for the fn body to know the mutability and do things differently - caller sees a single function, they still get to pick resulting mutability but the fn itself also knows the context.

For simple accessors it's not a problem, but still an unfortunate thorn to deal with - especially when polymorphism of other types is generally dealt with very well in Rust.

Ultimately, any function utilising generic mutability would be an accessor of some sort (possibly with side effects) - but some are more complex than others. A contrived example:

fn get_some_values(data: &Foo, iter: &mut Iterator<Item=SomeIndexer>) -> Vec<&Val> {
    let mut chosen_vals: Vec<&Val> = vec![];

    for i in iter { // Some sequence of index values
        let val = &data[i];

        // Add first-chosen value to `chosen_vals`, then insert subsequent vals
        // at a random place if some comparison between the currently-last val
        // succeeds
        if let Some(v) = data.last() {
            if some_comparison(v, val) {
                let insert_ind = rand(0, vals.len());
                vals.insert(insert_ind, val);
        } else {


Now imagine you need to also be able to receieve mutable output, where data is a &mut Foo.

You could minimise the repeated code by instead returning the chosen indexes from iter, instead of references directly. Then feed these index value into smaller getter functions:

fn get_some_values(data: &Foo, iter: &mut Iterator<Item=SomeIndexer>) -> Vec<&Val> {
    get_some_indices(data, iter).into_iter().map(|i| &data[i]).collect()

fn get_some_values_mut(data: &mut Foo, iter: &mut Iterator<Item=SomeIndexer>) -> Vec<&mut Val> {
    get_some_indices(data, iter).into_iter().map(|i| &mut data[i]).collect()

fn get_some_indices(data: &Foo, iter: &mut Iterator<Item=SomeIndexer>) -> Vec<Index> {
    let mut chosen_indices: Vec<&Val> = vec![];

    for i in iter {
        let val = &data[i];

        if let Some(v) = data.last() {
            if some_comparison(v, val) {
                let insert_ind = rand(0, vals.len());
                vals.insert(insert_ind, val);
        } else {


The final solution then:

  • Allocates an intermediate Vec
  • Performs indexing of data twice (which might be expensive)
  • Involves some non-trivial new getter functions

With mutability generics, this would have been a one-line change with no performance impact.

Good example. Yeah, I suppose traits can be used like this to achieve a kind of pseudo-overloading. @jonh's comment is correct however; my main reasoning behind this is to remove the need for the multitude of immut-then-mut traits and functions out there.

Vec<&Val> vs Vec<&mut Val> influences the types of Val references you can put into the Vec due to &mut changing variance. For instance, a certain Val type may not allow returning a &'static mut one and this influences code inside the fn as compiler would need to be conservative again.

1 Like

This seems like a perfect example of when generic mutability would be helpful! It's not what I would call trivial, so I don't see how you conclude that it "only really helps the trivial case of simple accessors"?

Unless you mean that 1% difference is why overloading is necessary? The only difference is some return type/value modification (default value instead of Option) at the end of the function, which is nothing to do with dealing with mut/non-mut types; it's just an some extra functionality that the programmer added to one of the functions. With generics, the immutable-with-default-val case would just be:

self.get_wall(point, direction).unwrap_or(Wallyness::Wall)

The return type is determined by the input provided by the caller, and as such the caller must be able to receive a value with the matching mutability. This is just the same as a trait or lifetime parameter being included in the return type. But you could interpret that as the reverse: mutability determined by return type, and the caller must be able to provide a matching input. :slight_smile:

This can be generally argued about trait/struct type generics too. It's a separate issue (the issue of overloading vs generics vs generics with reflection), and not specific to mutability.

Yes, a Vec<&mut Val> cannot take a reference to a subtype of Val, because that reference's data might be overwritten with an insufficient type. But as far as I can tell, this cannot happen with mutability generics because input data is supplied by the caller: In the mut case, input references will be to precisely the right type - suitable for a Vec<&mut T>. In the non-mut case, data might be a subtype - which is acceptable for the resultant Vec<&T>.

Maybe I've misunderstood the point. Could you provide an example where mutability generics cause a conflict with variance rules?

I meant that your approach requires the code in the body to be the same and only mutability is “forwarded” - that’s essentially a trivial function. The example was a case where the code should differ but only slightly based on mutability. This is not a trivial case but your proposal doesn’t work for it (the fact you can work around with unwrap_or in this particular case doesn’t generalize).

They don’t cause a conflict. It would limit the type of code you can write in the fn body. In the immutable case the code can stick a 'static; in the mutable they may not. It’s just an example of an extra restriction on the code.

But maybe that’s fine as what you’re suggesting is mutability forwarding or “passthrough” mutability. I’m not sure I’d consider it abstraction, at least in the same sense as AsRef/AsMut/Borrow/BorrowMut but perhaps exact nomenclature is unimportant.

I wonder how much of this can be implemented with associated type constructors and the family pattern:

trait RefFamily {
  type<'a, T> Ref : Deref<Target=T>;

struct ShrRef;
impl RefFamily for ShrRef {
  type<'a, T> Ref = &'a T;

struct MutRef;
impl RefFamily for MutRef {
  type<'a, T> Ref = &'a mut T;

struct Foo(i32, ...);
impl Foo {
  fn accessor<T: RefFamily>(self: T::Ref<Self>) -> T::Ref<i32>

So, we can write down the type of these accessors using associated type constructors. But we can probably not implement the accessors safely. But maybe with something like DerefField, that would be possible?

True, I hadn't considered this. A mutably-generic collection would also be unable to interact with other always-immutable arguments to the function. Previously, I was thinking any immutable-only function body would work, but now I can see that's not that case. :slight_smile:

Still, I think it could be worthwhile, obviously just for the cases which still work given this restriction.

Maybe that's not the right word then. Certainly, being able to pass-through mutability is the goal here, whatever that's called.

This is interesting. Not as neat as syntax-level support, but would be even more versatile. It's kind of a shame we already have so many FooRef/FooMut-type trait pairs... although maybe you could unify such pairs, as needed, using a similar family pattern?

A post was split to a new topic: Generic mutability test