Where is into_bytes_mut()?

Hello everybody,

this is probably going to be a stupid question on my part, but:

For Strings we can use

  • as_bytes() for an immutable borrow
  • as_bytes_mut() for a mutable borrow, which is unsafe as the bytes might be invalid utf8 at the end of the borrow
  • into_bytes() to consume the String and return an immutable u8 vector

This raises the question:
where is into_bytes_mut()?
A function that would give us a mutable u8 vector without being unsafe, as it consumes the String.

Seems to me like this should exist.

Cheers

There is no such thing as an "immutable vector" or a "mutable vector". Mutability is not a property of value types (except for references, which is exactly why two versions of as_bytes() need to exist).

Mutability is a property of bindings. If you want a mutable Vec<u8> out of a string, then write

let mut v = the_string.into_bytes();
9 Likes

Hahaha, I could facepalm myself here.
Thank you very much @H2CO3 !!!

Nit: it's also the same for references.

// valid
let mut p = &foo;
p = &bar;
// invalid
let p = &foo;
p = &bar; // error[E0384]: cannot assign twice to immutable variable `p`

What is true is that the transitive mutability may depend on the type. Both &T and &mut T are basically a pointer to T. However, &T doesn't give you mutable access to the pointed to value, while &mut T does.

So, in principle, there is nothing preventing the creation of immutable types. You would just have to declare a type which doesn't provide mutable access to its contents. Example:

pub mod foo {
    pub struct Foo(u32);

    impl Foo {
        pub fn new(x: u32) -> Foo { Foo(x) }
        pub fn into_inner(self) -> u32 { self.0 }
    }

    impl AsRef<u32> for Foo {
        fn as_ref(&self) -> &u32 {
            &self.0
        }
    }
}
use foo::Foo;

let mut f = Foo::new(1);
// We can replace the value entirely, since the binding is mutable.
f = Foo::new(2);
// But there is no way to mutate the wrapped `u32` 
// while keeping it inside `Foo`.
// The API allows us to unwrap `f` into a new variable, though.
let mut n = f.into_inner();
// Now we can mutate the integer.
n = 15;
// And wrap it back:
f = Foo::new(n);

Note that if your type is generic, then you likely cannot prevent the mutation of its contents, because the generic parameter may contain UnsafeCell and have interior mutability, i.e. it can be mutated through a &T. Currently there is no stable way to disallow such types, without introducing your own unsafe trait to track them.

I was perhaps not clear enough. What I meant is that there is no difference between Vec<u8> bound to an immutable and mutable binding (they have the same type), but there is a difference between &T and &mut T (they are not the same type). I did not mean to imply that there is a difference between a &T itself bound to a mutable or an immutable binding.

There is something deeply confused with your example. Mutating is the act of replacing an old value with a new value, which you just demonstrated. It's not clear what you mean operationally by "no way to mutate while keeping inside Foo" -- f was a Foo before and it is still a Foo after, what you have changed is what is "inside" the Foo, while seemingly claiming exactly that to be impossible.

If you meant your Foo to make it impossible to write a function that mutates a Foo through a &mut Foo reference, then it also fails:

fn mutate(foo: &mut Foo) {
    *foo = Foo::new(5);
}
1 Like

I meant exactly what I wrote: you can't mutate parts of an object unless there is some API which lets you do so. For example, if I make Foo::new private, then you can no loner construct a Foo object to replace the value in f.

I can phrase it as "you can't get &mut u32 to the value inside Foo unless the API allows you to". Being able to copy &u32 to a local variable and mutate that variable doesn't mean that you can mutate the contents of Foo.

Yes, but that would make Foo impossible to create at all rather than an "immutable type".

As long as you have two different instances of Foo, you can mutate mut variables and through &mut references, because you can always at least assign any one of those two instances to your variable.

That's true, for instance Vec doesn't give you such access to its capacity. But that's a different thing from saying Foo is an "immutable type".

I think you're trying to apply the mental model of an immutable type from languages such as Java, where e.g. String is an immutable type. That's not going to work. That analogy does not work in Rust. Java is different in this respect.

In Java, it's impossible to write a function that changes the value of a String given a reference to it:

// Java
void mutate(foo: String) {
  // impossible
}

My Rust mutate(foo: &mut Foo) implementation doesn't work in Java because crucially Java doesn't have the dereference operator *. It's important that it doesn't because in Java there is no distinction between unique and shared references.

But Rust is different: Rust does have the * operator, and it allows mutating anything to which you have a &mut reference or a mut binding regardless of its type.

I guess you could make an exception for unit types with only one possible value: those are indeed impossible to change.

1 Like

Not necessarily. You may get it from someone who has visibility on the constructor. Doesn't mean you can construct it.

use foo::Foo;
use bar::bar;

pub mod bar {
    pub fn bar(f: &mut super::Foo) {}
}

pub mod foo {
    pub struct Foo(u32);

    impl Foo {
        fn new(x: u32) -> Foo { Foo(x) }
        
        // We pass an instance of `Foo` to `bar`,
        // which can't construct an instance itself.
        pub fn use_foo(x: u32) {
            let mut f = Foo(x);
            super::bar(&mut f);
        }
    }

    impl AsRef<u32> for Foo {
        fn as_ref(&self) -> &u32 {
            &self.0
        }
    }
}

I would appreciate if you didn't try to put words in my mouth.

Here is an example of how one can implement immutable structures in Rust. Whether it is worth the effort is a separate question.

An "immutable data structure" is a slight misnomer here. A better name is "persistent data structure". In Rust terms it means that all important operations have efficient implementations that operate on immutable references, i.e. that there is efficient versioning capability, like in git.

It does not mean you can't modify the value of that type if you happen to own a unique reference to it - in Rust you can always do that. For instance std::mem::swap works for all types.

The name "immutable data structure" comes from the implementation strategy in other languages (like Java or ML) that lack the expressiveness of Rust and don't have unique references, but also don't have the dereference operator and hence can treat all references to that type as immutable as a means of conservatively protecting against shared mutation.

2 Likes

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.