如何构建Rc <str>或Rc&lt; [T]&gt;?

时间:2015-07-28 19:45:06

标签: rust

我想创建一个Rc<str>,因为我希望减少间接跟随访问Rc<String>所需的2个指针。我需要使用Rc因为我真的拥有共享权。我详细介绍了another question我在字符串类型中遇到的更多具体问题。

Rc has a ?Sized bound

pub struct Rc<T: ?Sized> { /* fields omitted */ }

我也听说过Rust 1.2会适当支持在Rc中存储未经过类型化的类型,但我不确定这与1.1的区别。

str为例,我的naive attemptthis用于从String构建)失败了:

use std::rc::Rc;

fn main() {
    let a: &str = "test";
    let b: Rc<str> = Rc::new(*a);
    println!("{}", b);
}
error[E0277]: the trait bound `str: std::marker::Sized` is not satisfied
 --> src/main.rs:5:22
  |
5 |     let b: Rc<str> = Rc::new(*a);
  |                      ^^^^^^^ `str` does not have a constant size known at compile-time
  |
  = help: the trait `std::marker::Sized` is not implemented for `str`
  = note: required by `<std::rc::Rc<T>>::new`

很明显,为了创建Rc<str>,我需要复制整个字符串:RcBox本身就是一个unsized类型,将字符串本身与弱指针和强指针一起存储 - 天真上面的代码甚至没有意义。

我被告知无法实例化此类型,而是实例化一个大小为Rc<T>的{​​{1}},然后将其强制转换为未大小的类型。给出的示例是存储特征对象:首先创建T然后强制转换为Rc<ConcreteType>。但这也没有意义:thisthis都不起作用(无论如何,你不能强迫Rc<Trait>&strString)。

2 个答案:

答案 0 :(得分:13)

从Rust 1.21.0开始,按照RFC 1845的要求,现在可以创建Rc<str>Arc<str>

use std::rc::Rc;
use std::sync::Arc;

fn main() {
    let a: &str = "hello world";
    let b: Rc<str> = Rc::from(a);
    println!("{}", b);

    // or equivalently:
    let b: Rc<str> = a.into();
    println!("{}", b);

    // we can also do this for Arc,
    let a: &str = "hello world";
    let b: Arc<str> = Arc::from(a);
    println!("{}", b);
}

Playground

请参阅<Rc as From<&str>><Arc as From<&str>>

答案 1 :(得分:9)

Creating an Rc<[T]> can be done via coercions and as-casts from fixed sized arrays, e.g. coercions can be done as follows:

use std::rc::Rc;

fn main() {
    let x: Rc<[i32; 4]> = Rc::new([1, 2, 3, 4]);

    let y: Rc<[i32]> = x;

    println!("{:?}", y);
}

However, this doesn't work for strings, since they have no raw fixed-sized equivalent to create the first value. It is possible to do unsafely, e.g. by creating a UTF-8 encoded Rc<[u8]> and transmuting that to Rc<str>. Theoretically there could be a crate on crates.io for it, but I can't find one at the moment.

An alternative is owning_ref, which isn't quite std::rc::Rc itself, but should allow, for example, getting an RcRef<..., str> pointing into an Rc<String>. (This approach will work best if one uses RcRef uniformly in place of Rc, except for construction.)

extern crate owning_ref;
use owning_ref::RcRef;
use std::rc::Rc;

fn main() {
    let some_string = "foo".to_owned();

    let val: RcRef<String> = RcRef::new(Rc::new(some_string));

    let borrowed: RcRef<String, str> = val.map(|s| &**s);

    let erased: RcRef<owning_ref::Erased, str> = borrowed.erase_owner();
}

The erasing means that RcRef<..., str>s can come from multiple different sources, e.g. a RcRef<Erased, str> can come from a string literal too.

NB. at the time of writing, the erasure with RcRef requires a nightly compiler, and depending on owning_ref with the nightly feature:

[dependencies]
owning_ref = { version = "0.1", features = ["nightly"] }