The Mutable counterpart of Cow container

In Rust std::borrow the Clone-on-Write container(Cow<'a, T>) allows us to pass the value either as owned, or as immutably borrowed. This container works transparently with Borrow.

I would like to understand why there is no "CowMut" counterpart for BorrowMut that would keep the borrowed variant as a mutable reference.

It is easy to express such type manually, but I'm not quite sure why it is not a part of the standard library? Is it some kind of an anti-pattern?

I can imaging the situations where such object will be useful. E.g. we have a function that accepts &'a mut T argument mutably, transforms this object, and returns data with 'a lifetime from transformed data (not necessary another reference). If we want this function to behave for 'static too, we would have to pass a static mutable reference to T. In most cases this is pointless when we just want to receive owned value of static lifetime from this function passing owned value of T.

1 Like

There is at least: https://crates.io/crates/mucow

There's no longer a need to clone-on-write, since it's always writable, so mucow calls itself clone-on-consume.

1 Like

I think there's just less use-cases / the use-cases are more niche. I've seen more Cow<'_, str> than any other, and &mut str is relatively limited (manipulating ASCII?), for example.

That said, I can think of some uses that take &mut MutCow [1] for the purposes of avoiding allocation as much as possible (e.g. I have a [T; N] that needs modified and maybe, but not always, grown (turned into a Vec<T>)). This is somewhat analogous to using Cow<'static, str> to avoid allocations.

Another standard use of Cow is an AsRef-like transformation that is lossy or otherwise can't always be performed, like to_string_lossy. That is, you return a Cow after taking in some other type.

For cases when you're only returning a MutCow, I see less motivation to not just change the source material in-place, since you're returning something that logically allows mutating in-place anyway. That doesn't cover situations similar to the previous point, where you don't actually have the owned variant, but a &mut [T] into a [T; N] or what-have-you. But it does mean there's less motivation for when you do have a &mut Vec<T>.


I've struggled to understand what you mean here, and couldn't think of something where this was necessary and made sense without leaking. What's the function signature you're thinking of, with and without MutCow?


  1. or takes and returns MutCow ↩ī¸Ž

2 Likes

I guess they didn't call it MuCoc because cocks don't moo, they crow.

3 Likes

I believe you are confusing the two meanings of 'static, like many people do.

You might have seen a 'static lifetime bound explained to you as it denoting an "owned" value. However, that's not the 'static lifetime parameter in reference types!

T: 'static means that instances of T are potentially valid to keep alive for the 'static lifetime (i.e., "forever"). This means that they don't contain any temporary pointers, i.e., they own their data.

In contrast, a &'static T means that you've got a pointer of which the referent really, actually lives forever. Of course this implies that T: 'static must be true per forza; however, they are not the same constraint.

A CowMut<'static, T> is perfectly possible to construct from, or turn into, an owned value. In this regard, 'static is just like any other lifetime, and in this case, there's also no difference between mutable and immutable references. CowMut wouldn't be any more powerful in achieving this than regular old Cow. The following compiles perfectly fine:

#[derive(Debug)]
enum CowMut<'a, T: ?Sized + ToOwned> {
	Borrowed(&'a mut T),
	Owned(T::Owned),
}

fn main() {
	let s = String::from("foo"); // definitely not lives for the 'static lifetime
	let cow: CowMut<'static, str> = CowMut::Owned(s);
	dbg!(cow);
}

Is it correct to say that a 'static lifetime bound on a type T requires that type T (not values of type T) to live forever, while a lifetime argument to a reference requires that reference to live forever, which implies the pointed-to value must live forever?

So

  • T: 'static ⇒ type T lives forever (but not necessarily values of type T)
  • &'static t ⇒ value t (e.g. of type T) lives forever

Did I get that right?

I don't think it makes (grammatical) sense to say that "a type lives for" any particular lifetime. Lifetimes are concerned with describing how long a value is around at runtime. A type itself doesn't "live" at runtime at all; it's a purely compile-time construct.

This isn't true, either – the lifetime argument on a reference is merely an upper bound on the possible lifetime of the reference itself. A &'static T itself isn't required to live for the 'static lifetime. It can live for any shorter lifetime as well. In contrast, its pointed value must actually live forever.

I feel like Cow<'a, T> is existent for the lifetime 'a only. It cannot be used afterwards. Even that being a compile-time property, I feel like it kinda(?) does make sense to say the type "lives" that long (following the terminology of lifetimes).

Isn't that only because of subtyping? Edit: but yeah, true nonetheless. I guess t would be required to live forever with &'static mut t.

No. Subtyping is why you can coerce a &'long T to a &'short T.

How long a value of type &'any T lives for is completely unrelated to subtyping – it's simple soundness logic. A pointer must not outlive its referent, but it's perfectly fine for it (the pointer itself) to exist for a shorter time. If you have a value, you don't have to point to it with something for the entirety of its lifetime!

1 Like

Ooops, I also saw that I mixed up t and T. In &'static T I meant a type T. So the T needs to be capitalized. Sorry for the confusion, I'll think about this again.

(So weird, I use these things all the time, yet get confused about them.)


Trying to rephrase:

  • T: 'static ⇒ a bound requiring that the type T "lives forever" (or exists forever, or could be used forever, etc. i.e. it has no lifetime parameters)
  • &'static T ⇒ this defines a type (a reference) which points to a value of type T, where the value must live forever

But yeah, maybe it's not the best way to say it. Thanks for correcting me on my mistakes above also.

Let me show a more practical example. We have a Box<dyn Any> type. For this type we have three different downcasting functions: by ref, by mut, and owned. In a nutshell they have similar meaning, and differ just in borrowing.

Let's introduce more polymorphic interface for downcasting:

enum CowMut<T, 'a> {
    Owned(T),
    Borrowed(&'a mut T)
}

trait Downcast<'a> {
    fn downcast(from: CowMut<'a, Box<dyn Any>>) -> Self
}

Now we can implement it for any type regardless of borrowing. E.g. for i32:

use CowMut::*;

impl<'a> Downcast<'a> for &'a i32 {
    fn downcast(from: CowMut<'a, Box<dyn Any>>) -> Self {
        match from {
            Owned(from) => unimplemented!(),
            Borrowed(from) => Box::downcast_ref::<i32>(from).unwrap(),
        }
    }
}

impl<'a> Downcast<'a> for &'a mut i32 {
    fn downcast(from: CowMut<'a, Box<dyn Any>>) -> Self {
        match from {
            Owned(from) => unimplemented!(),
            Borrowed(from) => Box::downcast_mut::<i32>(from).unwrap(),
        }
    }
}

impl Downcast<'static> for i32 {
    fn downcast(from: CowMut<'static, Box<dyn Any>>) -> Self {
        match from {
            Owned(from) => Box::downcast::<i32>(from).unwrap(),
            Borrowed(from) => unimplemented!(),
        }
    }
}

We cannot do the same thing with just Cow, because there is no mutable borrow variant. We of course may use Cow::to_mut instead but for mutable borrowing it would require turning borrowed data to owned in case of mutable downcasting. And what's important more is that with just &mut Cow we will not be able to pass owned data in general.

To further rationale the CowMut we may consider Cow itself.

What is Cow? In some way this is a sort of an object that plays a role of a data container and the RAII guard for itself. We can directly borrow it's inner data either by ref or by mut reference. Cow::deref would return an immutable borrowed into it's inner value, and Cow::to_mut would turn Cow into "RAII" guard for itself for mutably borrowing it's inner data. In either case we don't have intermediate explicit RAII object for borrowing.

In this sense Cow has established design pattern from standard library that combines the container and RAII guard, but we can easily imaging of spreading of this design pattern to custom RefCells and Mutexes, such as borrowing inner data would simply be a function with mutable receiver, that would turn the container into RAII guard and return inner borrowed data directly.

Returning to my Downcast example above. With such interface we need some kind of a CowMut interface that let us pass such Container+RAII object either borrowed, or owned. And we need a way to pass it as mutably borrowed, not immutably as in Cow, because the container assumed to change it's own variant in place.

T: 'static means that type T is subtype of type 'static. That's it. There is no other meaning, I do believe.

'static is not a type, and there is no subtyping between a type and a lifetime in Rust. (The only kind of subtyping is between types of the form T<'a> and T<'b> depending only on their variance and the lengths of the involved lifetimes.)

T: 'static is not a subtyping relationship. It is called a lifetime bound, and it means exactly what I wrote above.

This is just not true? &'static str is a subtype of &'a str.

With all due respect, I do think that both of your statements are not truly correct.

No, the T: 'static is a lifetime bound:

Lifetime bounds

[â€Ļ]

T: 'a means that all lifetime parameters of T outlive 'a. [â€Ļ]

And I agree with @H2CO3 that 'static is not a type.

Not sure if that was referring to the first past of your post or a general statement. Generally, Rust uses subtyping, but only in regard to lifetime differences. For example, &'static str is a subtype of &'a str (for any lifetime 'a).

Subtyping and Variance

Subtyping is implicit and can occur at any stage in type checking or inference. Subtyping is restricted to two cases: variance with respect to lifetimes and between types with higher ranked lifetimes. [â€Ļ]

Rust has some fancy names of top-level constructions. Perhaps, to improve the intuition behind the language design, but in a nutshell Lifetimes and e.g. Structs have no deep difference in a sense of subtype calculus. It is fair to call them the way they named in docs, but it does not contradict the fact that they all behave as just types in a nutshell.

Hmm, interesting!

A type cannot appear on the right-hand side of a bound like T: 'static. Maybe you mean to say that 'static is like a trait?

Well, from the type system point of view traits also can be seen as just types. That's why both traits and lifetimes can appear on the right hand side of A: B. Structs or enums cannot appear on the right hand side, but this is (again, from the type system point of view) because in Rust there is no inheritance for enums and structs. Structs and enums are the final subtypes of any other type, so having them on the right hand side is pointless. But if they could have subtypes, we would see them on the right hand side too.