Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to share parts of a string with Rc?

I want to create some references to a str with Rc, without cloning str:

fn main() {
    let s = Rc::<str>::from("foo");
    let t = Rc::clone(&s); // Creating a new pointer to the same address is easy
    let u = Rc::clone(&s[1..2]); // But how can I create a new pointer to a part of `s`?

    let w = Rc::<str>::from(&s[0..2]); // This seems to clone str
    assert_ne!(&w as *const _, &s as *const _);
}

playground

How can I do this?

like image 249
Yohei Avatar asked Oct 22 '25 06:10

Yohei


1 Answers

While it's possible in principle, the standard library's Rc does not support the case you're trying to create: a counted reference to a part of reference-counted memory.

However, we can get the effect for strings using a fairly straightforward wrapper around Rc which remembers the substring range:

use std::ops::{Deref, Range};
use std::rc::Rc;

#[derive(Clone, Debug, Eq, Hash, PartialEq)]
pub struct RcSubstr {
    string: Rc<str>,
    span: Range<usize>,
}

impl RcSubstr {
    fn new(string: Rc<str>) -> Self {
        let span = 0..string.len();
        Self { string, span }
    }
    fn substr(&self, span: Range<usize>) -> Self {
        // A full implementation would also have bounds checks to ensure
        // the requested range is not larger than the current substring
        Self {
            string: Rc::clone(&self.string),
            span: (self.span.start + span.start)..(self.span.start + span.end)
        }
    }
}

impl Deref for RcSubstr {
    type Target = str;
    fn deref(&self) -> &str {
        &self.string[self.span.clone()]
    }
}

fn main() {
    let s = RcSubstr::new(Rc::<str>::from("foo"));
    let u = s.substr(1..2);
    
    // We need to deref to print the string rather than the wrapper struct.
    // A full implementation would `impl Debug` and `impl Display` to produce
    // the expected substring.
    println!("{}", &*u);
}

There are a lot of conveniences missing here, such as suitable implementations of Display, Debug, AsRef, Borrow, From, and Into — I've provided only enough code to illustrate how it can work. Once supplemented with the appropriate trait implementations, this should be just as usable as Rc<str> (with the one edge case that it can't be passed to a library type that wants to store Rc<str> in particular).

The crate arcstr claims to offer a finished version of this basic idea, but I haven't used or studied it and so can't guarantee its quality.

The crate owning_ref provides a way to hold references to parts of an Rc or other smart pointer, but there are concerns about its soundness and I don't fully understand which circumstances that applies to (issue search which currently has 3 open issues).

like image 56
Kevin Reid Avatar answered Oct 24 '25 20:10

Kevin Reid



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!