Пытаясь скопировать содержимое Vec в другой, как использовать copy_from_slice ()? - PullRequest
0 голосов
/ 05 июня 2018

Я пытаюсь скопировать содержимое Vec в существующее Vec, заменив содержимое цели Vec.

Вот что я 'm ищет:

  • выделение новой памяти не требуется (если цель Vec слишком короткая), поскольку цель Vec уже выделена
  • , она должнане используйте итератор, потому что memcopy должен быть достаточно хорош для выполнения работы
  • источник Vec не должен быть изменен
  • в идеале он должен использовать безопасные методы

Вот что я попробовал:

  • vec.clone(): дает правильное содержимое, но выделяет новую память, которая не требуется для копирования в уже существующий достаточно большой Vec
  • vec.clear(); vec.extend();: копирует на месте, но, кажется, использует итератор для каждого элемента, который не требуется, я просто хочу, чтобы memcopy
  • vec.copy_from_slice(): это то, что я ищу, но мне нужноточно такие же размеры буферов, которые я почему-то не могу получить

Что дне работает

#![feature(shrink_to)]
fn vec_copy(src: &Vec<i32>, dst: &mut Vec<i32>) {
    // Try to adjust dst buffer size... there should be a better way
    if src.len() > dst.len() {
        let addon = src.len() - dst.len();
        dst.reserve_exact(addon);
    } else {
        dst.shrink_to(src.len());
    }
    // Do the copy        
    // panics! :
    // thread 'main' panicked at libcore/slice/mod.rs:1645:9
    // 'destination and source slices have different lengths'
    // 
    dst.copy_from_slice(src.as_slice()); // <--- panics here
}

fn main() {
    // Copy from a shorter Vec
    let mut container = vec![1, 2];
    let test1 = vec![3]; // shorter vec
    println!("{:p} = {:?}", &container[0], container); // output: 0x7f00bda20008 = [1, 2]
    vec_copy(&test1, &mut container); // panics inside function 
    println!("{:p} = {:?}", &container[0], container); // expected: 0x7f00bda20008 = [3]

    // Copy from a longer Vec
    container = vec![1, 2];    
    let test2 = vec![4, 5, 6]; // longer Vec
    println!("{:p} = {:?}", &container[0], container); // output: 0x7fef5b820018 = [1, 2]
    vec_copy(&test2, &mut container); // panics inside function
    println!("{:p} = {:?}", &container[0], container); // expected: 0x7fef5b820018 = [4, 5, 6]    
}

Паника с ошибкой:

thread 'main' panicked at libcore/slice/mod.rs:1645:9,
'destination and source slices have different lengths'

Вопрос

Использование vec.copy_from_slice(), кажется, способmemcopy помещает содержимое Vec в другое, без ненужного выделения памяти и без использования итератора.

Как установить размер цели Vec, чтобы vec.copy_from_slice() непаниковать?

Ответы [ 2 ]

0 голосов
/ 05 июня 2018

Вы хотите Vec::extend_from_slice:

fn vec_copy(src: &[i32], dst: &mut Vec<i32>) {
    // Optionally truncate to zero if there might be existing data
    // dst.clear();
    dst.extend_from_slice(src);
}

Это позволяет избежать необходимости заполнять вектор «фиктивными значениями» при добавлении большего количества элементов, чем было выделено ранее.

Если вы посмотрите на сборку в режиме выпуска, она вызывает memcpy после того, как выясняет, достаточно ли выделенного пространства:

playground::vec_copy:
    pushq   %r15
    pushq   %r14
    pushq   %r12
    pushq   %rbx
    subq    $88, %rsp
    movq    %rdx, %rbx
    movq    %rsi, %r15
    movq    %rdi, %r14
    movq    8(%rbx), %rsi
    movq    16(%rbx), %r12
    movq    %rsi, %rax
    subq    %r12, %rax
    cmpq    %r15, %rax
    jae .LBB1_14
    addq    %r15, %r12
    jb  .LBB1_8
    leaq    (%rsi,%rsi), %rax
    cmpq    %rax, %r12
    cmovbq  %rax, %r12
    movl    $4, %ecx
    movq    %r12, %rax
    mulq    %rcx
    jo  .LBB1_8
    testq   %rsi, %rsi
    je  .LBB1_9
    shlq    $2, %rsi
    movq    (%rbx), %rdi
    movq    %rsp, %r9
    movl    $4, %edx
    movl    $4, %r8d
    movq    %rax, %rcx
    callq   __rust_realloc@PLT
    testq   %rax, %rax
    jne .LBB1_5
    movq    (%rsp), %rax
    jmp .LBB1_12

.LBB1_9:
    movq    %rsp, %rdx
    movl    $4, %esi
    movq    %rax, %rdi
    callq   __rust_alloc@PLT
    testq   %rax, %rax
    je  .LBB1_12

.LBB1_5:
    xorl    %ecx, %ecx
    movdqa  32(%rsp), %xmm0
    movdqa  %xmm0, 48(%rsp)
    testq   %rcx, %rcx
    je  .LBB1_13

.LBB1_6:
    movq    %rax, (%rsp)
    movaps  48(%rsp), %xmm0
    movups  %xmm0, 8(%rsp)
    leaq    64(%rsp), %rdi
    movq    %rsp, %rsi
    callq   <core::heap::CollectionAllocErr as core::convert::From<core::heap::AllocErr>>::from@PLT
    movdqa  64(%rsp), %xmm0
    movq    %xmm0, %rax
    cmpq    $3, %rax
    je  .LBB1_14
    cmpq    $2, %rax
    jne .LBB1_15

.LBB1_8:
    leaq    .Lbyte_str.5(%rip), %rdi
    callq   core::panicking::panic@PLT
    ud2

.LBB1_12:
    movups  8(%rsp), %xmm0
    movaps  %xmm0, 32(%rsp)
    movl    $1, %ecx
    movdqa  32(%rsp), %xmm0
    movdqa  %xmm0, 48(%rsp)
    testq   %rcx, %rcx
    jne .LBB1_6

.LBB1_13:
    movq    %rax, (%rbx)
    movq    %r12, 8(%rbx)

.LBB1_14:
    movq    16(%rbx), %rdi
    leaq    (%rdi,%r15), %rax
    movq    %rax, 16(%rbx)
    shlq    $2, %r15
    shlq    $2, %rdi
    addq    (%rbx), %rdi
    movq    %r14, %rsi
    movq    %r15, %rdx
    callq   memcpy@PLT
    addq    $88, %rsp
    popq    %rbx
    popq    %r12
    popq    %r14
    popq    %r15
    retq

.LBB1_15:
    movq    80(%rsp), %rax
    movdqa  %xmm0, (%rsp)
    movq    %rax, 16(%rsp)
    movq    %rsp, %rdi
    callq   <alloc::heap::Heap as core::heap::Alloc>::oom
    ud2

println!("{:p} = {:?}", &container[0], container);

container.as_ptr() более очевиден, чем &container[0], и не потерпит неудачу, когда вектор пуст.

См. Также:

0 голосов
/ 05 июня 2018

Вы можете использовать функцию Vec.resize():

fn vec_copy(src: &Vec<i32>, dst: &mut Vec<i32>) {
    dst.resize(src.len(), 0);
    dst.copy_from_slice(src.as_slice());
}

fn main() {
    // Copy from a shorter Vec
    let mut container = vec![1, 2];
    let test1 = vec![3]; // shorter vec
    println!("{:p} = {:?}", &container[0], container); // output: 0x7f00bda20008 = [1, 2]
    vec_copy(&test1, &mut container); // panics inside function 
    println!("{:p} = {:?}", &container[0], container); // expected: 0x7f00bda20008 = [3]

    // Copy from a longer Vec
    container = vec![1, 2];    
    let test2 = vec![4, 5, 6]; // longer Vec
    println!("{:p} = {:?}", &container[0], container); // output: 0x7fef5b820018 = [1, 2]
    vec_copy(&test2, &mut container); // panics inside function
    println!("{:p} = {:?}", &container[0], container); // expected: 0x7fef5b820018 = [4, 5, 6]    
}

Выход:

0x7f2e36020008 = [1, 2]
0x7f2e36020008 = [3]
0x7f2e36020028 = [1, 2]
0x7f2e3602a010 = [4, 5, 6]

детская площадка

Конечный адрес имеет другой адрес, предположительно, потому что у места назначения была меньшая емкость, чем у источника, поэтому его пришлось увеличить, что означает перераспределение.Это можно показать, распечатав вместимость пункта назначения и заметив, что он вырос с 2 -> 4:

println!("{:p} = {:?} (capacity: {:?})", &container[0], container, container.capacity());
vec_copy(&test2, &mut container); // panics inside function
println!("{:p} = {:?} (capacity: {:?})", &container[0], container, container.capacity());

Вывод:

0x7f16a1820008 = [1, 2]
0x7f16a1820008 = [3]
0x7f16a1820028 = [1, 2] (capacity: 2)
0x7f16a182a010 = [4, 5, 6] (capacity: 4)

детская площадка

Вы можете предотвратить это, убедившись, что место назначения достаточно велико во время создания с помощью Vec::with_capacity.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...