【问题标题】:How to build an Rc<str> or Rc<[T]>?如何构建 Rc<str> 或 Rc<[T]>?
【发布时间】:2015-10-19 13:31:03
【问题描述】:

我想创建一个Rc&lt;str&gt;,因为我想减少访问Rc&lt;String&gt; 所需的2 个指针的间接性。我需要使用Rc,因为我确实拥有共享所有权。我在another question 中详细介绍了我在字符串类型方面遇到的更具体的问题。

RChas a ?Sized bound:

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

我还听说 Rust 1.2 将适当支持在 Rc 中存储未调整大小的类型,但我不确定这与 1.1 有何不同。

str 为例,我的naive attempt(也是this,用于从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&lt;str&gt;,我需要复制整个字符串:RcBox 本身就是一个未调整大小的类型,将字符串本身存储在弱指针和强指针旁边——上面的天真代码没有甚至没有意义。

有人告诉我不能实例化这种类型,而是用大小为TRc&lt;T&gt; 实例化一个T,然后将其强制为一个未调整大小的类型。给出的示例用于存储 trait 对象:首先创建 Rc&lt;ConcreteType&gt;,然后强制转换为 Rc&lt;Trait&gt;。但这也没有任何意义:thisthis 都不起作用(而且你不能从 &amp;strString 强制转换为 str)。

【问题讨论】:

  • 因为访问 Rc&lt;String&gt; 需要遵循 2 个指针(另外,我需要 Rc,因为我确实拥有共享所有权)。我详细介绍了in another question 我在字符串类型方面遇到的更具体的问题。我的暂定定义Rc&lt;Utf16Str&gt; 还需要一个未调整大小的类型Utf16Str(它的布局与Rc&lt;[u16]&gt; 相同)。

标签: rust


【解决方案1】:

从 Rust 1.21.0 开始,按照 RFC 1845 的要求,现在可以创建 Rc&lt;str&gt;Arc&lt;str&gt;

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)

参见&lt;Rc as From&lt;&amp;str&gt;&gt;&lt;Arc as From&lt;&amp;str&gt;&gt;

【讨论】:

    【解决方案2】:

    可以通过强制转换和as-casts 从固定大小的数组创建Rc&lt;[T]&gt;,例如强制可以如下进行:

    use std::rc::Rc;
    
    fn main() {
        let x: Rc<[i32; 4]> = Rc::new([1, 2, 3, 4]);
    
        let y: Rc<[i32]> = x;
    
        println!("{:?}", y);
    }
    

    但是,这不适用于字符串,因为它们没有原始固定大小的等价物来创建第一个值。有可能做不安全的事情,例如通过创建一个 UTF-8 编码的Rc&lt;[u8]&gt; 并将其转换为Rc&lt;str&gt;。理论上 crates.io 上可能有一个 crate,但我目前找不到。

    另一种选择是owning_ref,它本身并不完全是std::rc::Rc,但应该允许,例如,让RcRef&lt;..., str&gt; 指向Rc&lt;String&gt;。 (如果一个人统一使用RcRef 代替Rc,这种方法效果最好,但构造除外。)

    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();
    }
    

    擦除意味着RcRef&lt;..., str&gt;s 可以来自多个不同的来源,例如RcRef&lt;Erased, str&gt; 也可以来自字符串文字。

    注意。在撰写本文时,使用 RcRef 进行擦除需要夜间编译器,并且取决于 owning_refnightly 功能:

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

    【讨论】:

    • 所以在编译时 [T] 的长度未知时不能创建Rc&lt;[T]&gt;,对吧? (但为什么会这样?Rc 不是在堆中分配的吗?因为它是一个胖指针,它已经“知道”它的大小)
    • 是的,Rc&lt;[T]&gt; 不能通过动态长度的强制来构造。然而这个概念完全有道理,唯一的阻塞问题是没有好的方法来构建一个。
    • 另外,访问String 内的RcRef 是否需要以下两个指针? (整个问题是Rc&lt;String&gt; 有一个指向String 的指针,它有一个指向字符串开头的指针......)。我不知道this 是否只有一个指向String 的指针。
    • Re:“这个概念完全有道理”——这是否意味着除了Rc&lt;[T]&gt;Rc&lt;str&gt;(和其他未调整大小的类型)可以在未来版本上工作生锈?
    • @darque it already can 用于某些未确定大小的类型,例如特征。这是 Rust 1.1 IIRC 的新功能。
    猜你喜欢
    • 2019-12-13
    • 1970-01-01
    • 2023-01-20
    • 2022-08-16
    • 1970-01-01
    • 2020-01-15
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多