rustのArcについてその2
スライスからのArcの作成
Arcのソースをみていて気づきましたが,impl<T: Clone> From<&[T]> for Arc<[T]>
などが実装されており,CopyあるいはCloneが実装されているスライスからArcを作成することができます.(RCも同様です)
例えばこれを使うと,Arc<str>
(=Arc<[u8]>
)が作れます.
Arc<[T]>
はfat pointerとなり,構造的には以下の様になります.
x86_64環境なら最初の8byteがデータへのポインタ,そのあと8byteがデータのサイズです.
作成例
let s: Arc<str> = Arc::from("str"); println!("{:?}", as_raw_bytes(&s)); println!("{:?}", as_raw_bytes(&*s));
[64, 10, 247, 79, 223, 85, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0] [115, 116, 114]
Arc<String>
だと文字列にアクセスするまで二回ポインタを辿らないといけませんが(前回の記事参照),これなら一回で済みます.
BoxからのArcの作成
impl<T: ?Sized> From<Box<T>> for Arc<T>
も実装されています.
内部動作としてはBoxの中身をArc側にmemcpyして,box側のメモリを解放することになります.
こちらの場合,CopyあるいはCloneが実装されていなくてもArcが作成できます.T: ?Sized
なのでArc<[T]>
が作れます.
struct A(i32); let s: Box<[A]> = Box::new([A(0),A(1),A(2)]); let s: Arc<[A]> = Arc::from(s); println!("{:?}", as_raw_bytes(&s)); println!("{:?}", as_raw_bytes(&*s));
[224, 11, 247, 79, 223, 85, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0] [0, 0, 0, 0, 1, 0, 0, 0, 2, 0, 0, 0]
まぁこの場合そもそも普通に Arc::new([A(0), A(1), A(2)])
すればいいんですが.
Arc<str>
をString
から作成したい場合は,String::into_boxed_str()
を使ってBox<str>
にしてからArc
にできます.
let s: Arc<str> = Arc::from(format!("str").into_boxed_str()); println!("{:?}", as_raw_bytes(&s)); println!("{:?}", as_raw_bytes(&*s));
[192, 11, 247, 79, 223, 85, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0] [115, 116, 114]
VecからのArcの作成
impl<T> From<Vec<T>> for Arc<[T]>
もあります.
let s: Arc<[i32]> = Arc::from(vec![0,1,2]); println!("{:?}", as_raw_bytes(&s)); println!("{:?}", as_raw_bytes(&*s));
[16, 12, 247, 79, 223, 85, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0] [0, 0, 0, 0, 1, 0, 0, 0, 2, 0, 0, 0]
こちらも動作的にはArcが新規に割り当てたメモリ領域にVecのデータを全部移すことになります.
servo_arc
servoにはservo_arcという,Arcの派生実装が含まれています.
以下の様な特徴があります.
- weakカウントがなくてstrongカウントのみ
- dynamically-sized type (DST)のサポート
DSTに関しては以前記事を書きました.servo_arc
を使うと,以下の様なことができます.
#[derive(Debug)] struct Header { a: u32, b: u32, c: u32, }; let header = Header { a: 1, b: 2, c: 3 }; let data: Vec<u32> = vec![4, 5, 6]; let a = servo_arc::Arc::from_header_and_iter(header, data.into_iter()); println!("{:?}", &a); println!("{:?}", as_raw_bytes(&a)); println!("{:?}", as_raw_bytes(&*a));
HeaderSlice { header: Header { a: 1, b: 2, c: 3 }, slice: [4, 5, 6] } [240, 44, 64, 90, 168, 127, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0] [1, 0, 0, 0, 2, 0, 0, 0, 3, 0, 0, 0, 4, 0, 0, 0, 5, 0, 0, 0, 6, 0, 0, 0]
Arc::from_header_and_iter()
はArc<HeaderSlice<H, [T]>>
を作成します.HeaderSliceは以下の様に定義されます.
pub struct HeaderSlice<H, T: ?Sized> { /// The fixed-sized data. pub header: H, /// The dynamically-sized data. pub slice: T, }
Arc<HeaderSlice<H, [T]>>
は以下の様な構造になります.
これの何が嬉しいのかというと,zero-length arrayを受け取るCの関数をFFIで呼ぶ時なんかに便利かなと思います.servoではどう使われてるんでしょう(未確認)