Open16

rust-ndarrayの所有権周りのコードを読んだ

bokutotubokutotu

GPU用の多次元配列及び線形代数ライブラリをRustで作りたいと思い、rust-ndarrayのソースを読んでみた。

bokutotubokutotu

rust-ndarrayはarrayを所有している場合、可変参照の場合、参照の場合をそれぞれDataの持ち方に関する構造体で管理している。

データを所有している場合、OwendRepr,データを可変参照または参照の場合はVeiwReprで管理している。
参照の場合はViewRepr<&'a A>,可変参照の場合はViewRepr<&'a mut A>になる。
それぞれに対してArray, ArrayView, ArrayViewMutと型エイリアスしてある。

bokutotubokutotu

OwendReprは重いのでまず、ViewReprを読む。

pub struct ViewRepr<A> {
    life: PhantomData<A>
}

となっていて型を消費しているだけ。

bokutotubokutotu

次にOwnedReprを読む

pub struct OwnedRepr<A> {
    ptr: NonNull<A>,
    len: usize,
    capacity: usize
}

となっている。
実際にポインタを持っている。
でも、ArrayBaseでもポインタ持ってる。
ArrayBaseのポインタは実際に確保されているポインタからoffsetされてる可能性でもあるのかなぁ?

pub struct ArrayBase<S, D>
    where
        S: RawData,
{
    data: S
    ptr: std::ptr::NonNull<S::Elem>,
    dim: D,
    strides: D,
}

となっている

bokutotubokutotu

OwnedReprの中を読んでみた。

  • コンストラクタ
    • from
  • ポインタ系
    • as_ptr
    • as_ptr_mut
    • as_nunnull_mut
    • as_end_nonnull
  • データ領域いじる系
    • reseve
    • set_len
  • 中身いじる系
    • modefy_as_vec
  • データ取り出す系
    • take_as_vec
    • into_vec
    • as_slice
  • len

など
データを所有してないとダメそうなやつはある気がする。
てか、RawDataとか他のクレートで制限をつけてるけどこれだけで良くない?と思ったりする

bokutotubokutotu

ArrayBaseのdataに関するものとしてはdata_trait.rs内のトレイトも重要そうなので読む。
トレイトが何個かあって、以下のような関係になってるっぽい。

bokutotubokutotu

RawDataを読む
deeplさん頼んだ

配列表現特性。

ArrayBase型の不変量に合致する配列の場合。この特性は、いかなる所有権や寿命も意味しない。配列の要素へのポインタは、安全に参照解除できないかもしれない。

注意:RawDataは、現時点では拡張インターフェースではありません。Rustのtraitは、様々な役割を果たすことができます。この特性は、パブリックメソッドのバインドとして使用されるため、パブリックです。

よくわからない。
つまりは、ArrayBase関する特性を表すトレイトである。
ArrayBaseのinvariants(この単語は”不変”のという意味らしいがよくわからん)に当たる場合のためのもの。
このトレイトは所有権とライフタイムの情報も持たない(基底クラスだから?)

すべての所有権、ライフタイム周りの構造体にimplされている

メソッドは

  • _data_slice(deprecated)
  • _is_poionter_inbounds

がある

ArrayBaseの基底となるクラスが必要なのはわかるけど、なぜそこに_is_pointer_inboundsが必要なのかはよくわかっていない。

あと、private_decl!()マクロでpubなトレイトでも外部からは実装できなくなっている

bokutotubokutotu

RawDataMutを読む
RawDataをトレイト境界としてもつ
書き込み可能なarrayに対するものっぽい。

メソッドは

  • try_ensure_unique
  • try_is_unique

それぞれのメソッドは、
可能であれば、そのデータへのアクセスが単一かどうかを返すメソッドである。
try_ensure_uniqueに関してはOwendArcReprCowRepr以外はそのまま返している。
そのトレイとが実装されている場合はデータへのアクセスはユニークである。
同様に、try_is_uniqueOwnedArcReprCowRepr以外はSome(true)を実装する

implされているのは

  • OwnedRepr
  • ViewRepr<&'a mut A>

ほかの部分は後で読む

RawDataMutが実装されているものに関してはもともとそのオブジェクトを作成する際に、
mutであることがわかっているのでこのトレイとの必要性がよくわからない。

RawViewOwnedArcReprなどではチェックが入る場合があるっぽいからのそのためなのかねぇ?

bokutotubokutotu

RawDataClone
名前の通りCloneできるものに対して実装する

implされているのは

  • OwnedRepr
  • ViewPrepr<&'a A>
bokutotubokutotu

Data
データへのアクセスをsafeに行える

データへのアクセスがsafeであることとinto_owedtry_into_onwed_nocopyto_sharedの関連性がよくわからない。

どちらかというと、所有していないArrayBaseを所有するためのトレイトなような気がする

bokutotubokutotu

DataMut
Data + RawDataMut

safeにデータにアクセスすることが可能でなおかつ書き換え可能なトレイトであること

bokutotubokutotu

ここまで読んでいた感想としては、ArcArrayのための昨日が大きいのでは?という気がする
ほかならそんなに気にせず使えるような気がする

bokutotubokutotu

git grep S::してみる(RawData系のトレイトを使われている先を探す)

ndarray-rand/src/lib.rs:        IdS: Distribution<S::Elem>,
ndarray-rand/src/lib.rs:        IdS: Distribution<S::Elem>,
ndarray-rand/src/lib.rs:        IdS: Distribution<S::Elem>,
ndarray-rand/src/lib.rs:        IdS: Distribution<S::Elem>,
src/array_serde.rs:    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
src/arraytraits.rs:    type Output = S::Elem;
src/arraytraits.rs:    fn index(&self, index: I) -> &S::Elem {
src/arraytraits.rs:    fn index_mut(&mut self, index: I) -> &mut S::Elem {
src/arraytraits.rs:    S::Elem: Eq,
src/arraytraits.rs:    type Item = &'a S::Elem;
src/arraytraits.rs:    type IntoIter = Iter<'a, S::Elem, D>;
src/arraytraits.rs:    type Item = &'a mut S::Elem;
src/arraytraits.rs:    type IntoIter = IterMut<'a, S::Elem, D>;
src/arraytraits.rs:    S::Elem: hash::Hash,
src/impl_constructors.rs:    pub fn uninit<Sh>(shape: Sh) -> ArrayBase<S::MaybeUninit, D>
src/impl_constructors.rs:    pub fn build_uninit<Sh, F>(shape: Sh, builder: F) -> ArrayBase<S::MaybeUninit, D>
src/impl_methods.rs:        S::to_shared(self)
src/impl_methods.rs:        S::into_owned(self)
src/impl_methods.rs:        S::try_into_owned_nocopy(self)
src/impl_methods.rs:        S::try_ensure_unique(self);
src/impl_methods.rs:        S::ensure_unique(self);
src/impl_special_element_types.rs:        let data = S::data_subst(data);
src/lib.rs:/// fn fill_lower<S, D>(arr: &mut ArrayBase<S, D>, x: S::Elem)
src/lib.rs:///     S::Elem: Clone,
src/lib.rs:    ptr: std::ptr::NonNull<S::Elem>,
src/linalg/impl_linalg.rs:    S::Elem: 'static,
src/linalg/impl_linalg.rs:    if !same_type::<A, S::Elem>() {
src/linalg/impl_linalg.rs:    S::Elem: 'static,
src/linalg/impl_linalg.rs:    if !same_type::<A, S::Elem>() {
src/linalg/impl_linalg.rs:    S::Elem: 'static,
src/linalg/impl_linalg.rs:    if !same_type::<A, S::Elem>() {
src/linalg/impl_linalg.rs:    S::Elem: 'static,
tests/array.rs:        S::Elem: Clone + Debug + PartialEq,
bokutotubokutotu

ensure_uniquetry_ensure_uniqueViewReprOnwedReprでは必ず同じものを返す
つまりは、ArcRawをいい感じに扱うためにできたものである可能性が高い

bokutotubokutotu

まず、RawDataを作る必要あるんですかね?

bokutotubokutotu

SliceでArrayViewなどを返すときにViewを実装してないと難しい