Home > Back-end >  Does slicing a string copy the underlying data?
Does slicing a string copy the underlying data?

Time:12-24

In Rust, if I wanted read-only access to one &str across multiple contexts, without copying the actual underlying data, am I correct in thinking this I just use a slice?

Example

let original_string = "here's a string";

let slice = &original_string[0..3];

Or would something like Rc<str> be needed?

CodePudding user response:

Any reference type &T or &mut T (including slice types like &str or &[T]) will only borrow data, and will not implicitly copy or move data when used.

You can explicitly copy from a reference to a type implementing Copy by dereferencing it with *, or from a reference to a type implementing Clone by calling the clone method. You can also explicitly move out of a mutable reference by replacing it with a different value, for example with std::mem::take or std::mem::replace.

Any data that's borrowed by at least one shared reference &T must be read only for the lifetime of that reference, except for data inside a container with interior mutability, such as Cell<T>, RefCell<T>, Mutex<T> and AtomicU32.

Typically, the biggest limitation of using references is that their lifetime must be shorter than the lifetime of the owner of the data. In some cases, this may make code hard or impossible to express purely with references and lifetimes. Reference-counting pointers like Rc<T> and Arc<T> can help, as they generally act similar to shared references &T, except that instead of borrowing data from some other location, the data is moved into the Rc/Arc and then the data ownership is shared between all clones of that Rc/Arc, elimitating the need for reference lifetimes, but at a small runtime cost.

CodePudding user response:

read-only access to one &str

you are right, the codes did not copy the string, &str basically includes 2 parts, a pointer and a len, thus

    let original_string = "here's a string";
    let slice = &original_string[0..3];

    println!("{:?}", original_string.as_ptr());
    println!("{:?}", original_string.len());
    println!("{:?}", slice.as_ptr());
    println!("{:?}", slice.len());

The output would look like

0x1074d4ee3
15
0x1074d4ee3
3

CodePudding user response:

Slicing a string does not copy, it borrows. Which also means you'll have to keep the original string alive as least as long as the slice. (Frxstrem's answer explains this way better.)

Rc<str> won't help you, since it must be an Rc on the same string or slice, i.e. you can't create a subslice but count references on the same base string with it. If you do absolutely need something like this, you'll have to store an Rc on the original string, and the current slice range with it, e.g. like this:

#[derive(Clone)]
struct SharedString {
    string: Rc<str>,
    slice: Range<usize>,
}

impl Deref for SharedString {
    type Target = str;
    fn deref(&self) -> &str {
        self.string.get(self.slice.clone()).unwrap()
    }
}

Playground

  • Related