Return mutable slice from enum method


#1

Hello,
I want to keep slice in Range enum and get sublice defined by parameters of the enum. The immutable part compiles and work great but the mutable part makes me cry for a couple of hours already. How can I return a mutable slice from get_mut?

The error returned by rustc.

src/main.rs:18:32: 18:36 error: cannot borrow immutable borrowed content `**data` as mutable
src/main.rs:18             Range::All(ref mut data) => data,
                                                       ^~~~
src/main.rs:19:51: 19:55 error: cannot borrow immutable borrowed content `**data` as mutable
src/main.rs:19             Range::Window(ref mut data, _from, _length) => data,
                                                                          ^~~~

The test program.

use std::slice;

enum Range<'b> {
    All(&'b [u8]),
    Window(&'b [u8], usize, usize),
}

impl<'b> Range<'b> {
    fn get(&self) -> &'b [u8] {
        match *self {
            Range::All(ref data) => data,
            Range::Window(ref data, from, length) => unsafe { slice::from_raw_parts(&data[from], length) },
        }
    }

    fn get_mut(&mut self) -> &'b mut [u8] {
        match *self {
            Range::All(ref mut data) => data,
            Range::Window(ref mut data, _from, _length) => data,
        }
    }
}

fn main() {
    let data = b"Test!";
    println!("All {:?}", String::from_utf8(Range::All(data).get().to_vec()));
    println!("Window {:?}", String::from_utf8(Range::Window(data, 1, 3).get().to_vec()));
}

Thank you for any piece of information.


#2

TLDR: this compiles

use std::slice;

enum Range<'b> {
    All(&'b mut [u8]),
    Window(&'b mut [u8], usize, usize),
}

impl<'b> Range<'b> {
    fn get(&self) -> &[u8] {
        match *self {
            Range::All(ref data) => data,
            Range::Window(ref data, from, length) => unsafe { slice::from_raw_parts(&data[from], length) },
        }
    }

    fn get_mut(&mut self) -> &mut [u8] {
        match *self {
            Range::All(ref mut data) => data,
            Range::Window(ref mut data, _from, _length) => data,
        }
    }
}

fn main() {
    let mut data = [92u8; 8];
    {
        let range = Range::All(&mut data);
        println!("All {:?}", String::from_utf8(range.get().to_vec()));
    }
    {
        let range = Range::Window(&mut data, 1, 3);
        println!("Window {:?}", String::from_utf8(range.get().to_vec()));
    }
}

Long version will follow :slight_smile:


#3

There are several problems here, and Rust really saves you from doing something dangerous, as usual :slight_smile:

Let’s look closer at the main method. Here you have a byte literal data. It is stored in a read only data segment of the binary. So if your code compiled, then via get_mut you would be able to mutate read only memory, which is not a good thing.

If you want to get mut references out of a Range, you need to store mut references. Hence the All(&'b mut [u8]) change.

Next, you don’t need to specify output lifetime for get method. There are some complex sounding rules about lifetime elision, but there is a simple case. If a function takes a reference as an input, and produces a reference as an output, then their lifetimes are assumed to be the same, because the function usually can’t produce a lifetime out of a thin air. Hence fn get(&self) -> &[u8] change.

Next, in main we want some byte slice that we can actually mutate. A byte string literal won’t do, because it resides in the read only memory and is not mutable. So let’s use a stack allocated array instead [92u8, 8]. You can also init it as [b'H', b'e', b'l', b'l', b'o']. BTW, does anybody knows a syntax to init a stack allocated array with b"Hello, World" more concisely?

This should cover all lifetime problems.

However, this unsafe block is bothering me :slight_smile: Usually, the only really legitimate reason to use unsafe is to call some C function. In almost all other cases you don’t need unsafe. And you don’t need unsafe for performance reasons. I sometimes find myself justifying the use of unsafe for speed reasons only to find a totally safe solution which is just as fast, but shorter and simpler. (This is my opinion, I can’t prove that this is indeed true)

In this particular case, unsafe { slice::from_raw_parts(&data[from], length) } can be replaced with &data[from..from + length]. And this provokes a question: why do you need this Range enum at all? Looks like the slice should be enough here.

Cheers!


#4

Awesome! Thank you very much!

I’d like ot use Range enum to pass slices and ranges to take action on in functions. Like we have a sline 1024 bytes in long and some function should take action on say first 10 bytes. Yes, I see now that I can pass… hmmm… subslice simpler using syntax &data[0..10] but Range::Window(&mut data, 0, 10) looks more informative. But that is my opinion.


#5

The nice thing about slices is not just the syntax, but that the standard library is packed with handy functions that apply to them, right out of the box.


#6

Yes, thank you. It seems slices is the better way to handle… slices. :slightly_smiling: