2017-10-31 24 views
3

Vec::insert_slice(index, slice)メソッドが必要でした。文字列の解決法(String::insert_str())は存在します。Vecにスライスを追加する方法

私は約Vec::insert()を知っていますが、スライスではなく、一度に1つの要素しか挿入しません。代わりに、プリペンドされたスライスがVecの場合、代わりに追加することもできますが、これは一般化しません。慣用的な解決法はおそらくVec::splice()を使用しますが、この例のようにイテレータを使用すると私の頭が傷つきます。

第2に、プリペンドの全体概念は、外見上、docsから退去されました。一つの言及はありません。私は理由についてのコメントを感謝します。 Vec::swap_remove()のような比較的あいまいなメソッドが存在することに注意してください。

私の典型的な使用例は、インデックス付きバイト文字列で構成されています。

+2

[効率的の真ん中に複数の要素を挿入Vec](https://stackoverflow.com/questions/28678615/efficiently-insert-multiple-elements-in-the-middle-of-a-vec)? – trentcl

+0

はい、prependingは特殊なケースです。 – user103185

+0

@trentcl良い目!あなたは重複としてマークするべきだと思いますか? – Shepmaster

答えて

4

String::insert_strは、文字列が本質的にVec<u8>であるという事実を利用しています。それはreallocates the underlying buffer, moves all the initial bytes to the end, then adds the new bytes to the beginningです。

Vecはもはや有効な状態になっていないため、通常は安全ではなく、直接Vecに追加することはできません。データに「穴」があります。

データはu8u8Dropを実装していないので、これはStringのために重要ではありません。 Vecに任意のTの保証はありませんが、あなたの状態を追跡して適切にクリーンアップする場合は、同じことをすることができます - これはspliceです。

先頭追加の全体のコンセプトは、一見

をexorcisedてきた私はVecに先頭に追加すると、パフォーマンスの観点から、貧しい発想であるためであると仮定したいです。あなたがそれを行う必要がある場合は、ナイーブ場合は、ストレートフォワードです:

fn prepend<T>(v: Vec<T>, s: &[T]) -> Vec<T> 
where 
    T: Clone, 
{ 
    let mut tmp: Vec<_> = s.to_owned(); 
    tmp.extend(v); 
    tmp 
} 

我々はvの2つのコピーのための十分なスペースを持っている必要がありますので、これは少し高く、メモリ使用量があります。

spliceメソッドは、新しい値のイテレータと置き換える値の範囲を受け入れます。この場合、何も置き換えたくないので、挿入するインデックスの空の範囲を指定します。我々はまた、適切な型のイテレータにスライスを変換する必要があります。

let s = &[1, 2, 3]; 
let mut v = vec![4, 5]; 

v.splice(0..0, s.iter().cloned()); 

splice's implementation is non-trivialが、それは効率的に私たちに必要な追跡を行います。値の塊を取り除いた後、は、新しい値のためのメモリの塊を再利用します。また、ベクトルの末尾を(おそらく数回、入力イテレータに応じて)動かします。 Dropの実装がSliceの場合、常に有効な状態になります。


私は、データの先頭と末尾の両方の変更についてのより効率的に設計されていますようVecDequeは、それをサポートしていないことが、より驚いています。Shepmasterが言ったことを考慮すると

+0

「穴」の説明に感謝します。 spliceメソッドがあり、またRFC(1964年と1432年)に関連していることに注意してください。 – user103185

+0

@ user103185ああ、スプライスはとても新しく、私はあなたの元の質問でそれを逃しました。それを含むように更新されました。 – Shepmaster

1

、あなただけのString::insert_str()ようVecCopyできる要素を持つスライスを付加機能を実装することができ、次のように行います。

use std::ptr; 

unsafe fn prepend_slice<T: Copy>(vec: &mut Vec<T>, slice: &[T]) { 
    let len = vec.len(); 
    let amt = slice.len(); 
    vec.reserve(amt); 

    ptr::copy(vec.as_ptr(), 
       vec.as_mut_ptr().offset((amt) as isize), 
       len); 
    ptr::copy(slice.as_ptr(), 
       vec.as_mut_ptr(), 
       amt); 
    vec.set_len(len + amt); 
} 

fn main() { 
    let mut v = vec![4, 5, 6]; 

    unsafe { prepend_slice(&mut v, &[1, 2, 3]) } 

    assert_eq!(&v, &[1, 2, 3, 4, 5, 6]); 
} 
+0

私は十分ではありませんが、ゼロサイズのタイプについて心配しています。 – Shepmaster

+0

@Shepmaster:面白い点;空の構造体(これはうまくいった)でテストしましたが、これがすべての可能性をカバーしているかどうかはわかりません。私はそれが通常のプリミティブのために安全でなければならないと思う。 – ljedrz

関連する問題