Подтвердить что ты не робот

Как построить Rc <str> или Rc <[T]>?

Я хотел бы создать Rc<str>, потому что я хочу уменьшить косвенность из-за двух указателей, требующих доступа к Rc<String>. Мне нужно использовать Rc, потому что у меня действительно есть совместное владение. Я детализирую в еще один вопрос более конкретные проблемы, которые возникают у моего типа строк.

Rc имеет ?Sized bound:

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

Я также слышал, что Rust 1.2 придет с надлежащей поддержкой для хранения нестандартных типов в Rc, но я не уверен, как это отличается от 1.1.

Взяв пример str в качестве примера, моя наивная попытка (также 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<str> мне нужно скопировать всю строку: RcBox будет сам по себе несимметричным типом, сохраняя саму строку рядом с слабыми и сильными указателями - наивный код выше не делает " t даже имеет смысл.

Мне сказали, что невозможно создать такой тип, но вместо этого создайте экземпляр Rc<T> с размером T и затем принудительно замените его на нестандартный тип. Приведенный пример предназначен для хранения объекта-объекта: сначала создайте Rc<ConcreteType>, а затем принудителю к Rc<Trait>. Но это тоже не имеет смысла: ни this, ни this (и вы не можете принуждать от &str или String до str) в любом случае.

4b9b3361

Ответ 1

Создание Rc<[T]> может быть выполнено с помощью принуждений и as -casts из массивов фиксированного размера, например. принуждения могут быть выполнены следующим образом:

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<[u8]> и преобразуя его в Rc<str>. Теоретически на ящиках может быть ящик. Я не могу найти его на данный момент.

Альтернативой является owning_ref, который не совсем сам std::rc::Rc, но должен позволить, например, получить RcRef<..., str>, указав на Rc<String>. (Этот подход будет работать лучше всего, если использовать 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<..., str> может поступать из нескольких разных источников, например. a RcRef<Erased, str> может также поступать из строкового литерала.

NB. на момент написания стирание с RcRef требует ночного компилятора и в зависимости от owning_ref с функцией nightly:

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

Ответ 2

Как и в случае 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);
}

(Игровая площадка)

См. <Rc as From<&str>> и <Arc as From<&str>>.