2015-12-20 2 views
5

Параметры могут быть переданы функции и изменения:Как изменить отрезок, который является параметром функции?

fn set_42(int: &mut i32) { 
    *int += 42; 
} 

fn main() { 
    let mut int = 0; 
    set_42(&mut int); 
    println!("{:?}", int); 
} 

Output:

42 

Наивно изменить код, чтобы использовать ломтик терпит неудачу с целой кучей ошибок:

fn pop_front(slice: &mut [i32]) { 
    *slice = &{slice}[1..]; 
} 

fn main() { 
    let mut slice = &[0, 1, 2, 3][..]; 
    pop_front(&mut slice); 
    println!("{:?}", slice); 
} 

Output:

<anon>:2:14: 2:27 error: mismatched types: 
expected `[i32]`, 
    found `&[i32]` 
(expected slice, 
    found &-ptr) [E0308] 
<anon>:2  *slice = &{slice}[1..]; 
         ^~~~~~~~~~~~~ 
<anon>:2:14: 2:27 help: see the detailed explanation for E0308 
<anon>:2:5: 2:11 error: the trait `core::marker::Sized` is not implemented for the type `[i32]` [E0277] 
<anon>:2  *slice = &{slice}[1..]; 
      ^~~~~~ 
<anon>:2:5: 2:11 help: see the detailed explanation for E0277 
<anon>:2:5: 2:11 note: `[i32]` does not have a constant size known at compile-time 
<anon>:2:5: 2:11 note: the left-hand-side of an assignment must have a statically known size 
error: aborting due to 2 previous errors 

Если попытаться использовать изменяемый срез (который не то, что я действительно хочу; Я не хочу, чтобы изменить значение в срезе, я просто хочу, чтобы изменить сам срез так оно охватывает меньший диапазон элементов) и изменяемый параметр, он не имеет никакого влияния на оригинальном срезе:

fn pop_front(mut slice: &mut [i32]) { 
    slice = &mut {slice}[1..]; 
} 

fn main() { 
    let mut slice = &mut [0, 1, 2, 3][..]; 
    pop_front(&mut slice); 
    println!("{:?}", slice); 
} 

Output:

[0, 1, 2, 3] 

Вопрос: есть ли способ изменить кусочек это параметр функции? Я не хочу изменять элементы внутри среза; Я просто хочу изменить диапазон самого среза, чтобы он стал меньшим «суб-срезом».

ответ

5

Как уже говорилось, основная идея здесь взять &mut &... [T] (где ... является mut или пустой) и чтения/записи на внутренний срез. Остальные ответы показывают, что можно использовать код &mut &[T] в безопасном коде и возможно для &mut &mut [T] с небезопасным кодом, но они не объясняют, почему существует разница ... и &mut &mut [T] возможен и с безопасным кодом.

В явной пожизненных сроков, вложенная ссылка что-то вроде &'a mut &'b ... [T] для некоторых воплощений 'a и "b, и цель здесь, чтобы получить &'b ... [T], нарезать его и написать, что в &'a mut.

Для &'a mut &'b [T], это легко: &[T] является копией, поэтому писать *slice = &slice[1..] эффективно скопировать &'b [T] из &mut, а затем, позже, перезаписать существующее значение с тем короче один. Копия означает, что один буквально получает &'b [T] для работы, и поэтому нет прямой связи между этим и &'a mut, и, следовательно, законно мутировать. Это эффективно что-то вроде

fn pop_front<'a, 'b>(slice: &'a mut &'b[i32]) { 
    // *slice = &slice[1..] behaves like 
    let value: &'b [i32] = *slice; 
    *slice = &value[1..] 
} 

(я пометил время жизни и аннотированный тип, чтобы связать в мое объяснение, но это не требуется для кода для работы.)

Для &'a mut &'b mut [T] вещей являются немного сложнее: &mut [T] не может быть скопировано: разыменования не будет копировать, он будет перезанимать, чтобы дать &'a mut [T] т.е. срез имеет срок службы, который подключен к внешнему&'a mut, а не внутренний &'b mut [T]. Это означает, что нарезанная ссылка имеет более короткий срок службы, чем тип, который она пытается перезаписать, поэтому недопустимо хранить срез в этой позиции. Другими словами:

fn pop_front<'a, 'b>(slice: &'a mut &'b mut [i32]) { 
    let value: &'a mut [i32] = &mut **slice; 
    *slice = &mut value[1..] // error 
} 

Способ сделать это безопасно для &'a mut &'b mut [T], чтобы получить внутренний срез из ссылки с этой 'b жизни. Это требует отслеживания правила «одного владельца», без заимствований, и лучшая функция для такого манипулирования владением - mem::replace. Это позволяет нам извлечь внутренний &'b mut [T], заменив его каким-то заполнителем, который мы можем затем перезаписать короткой версией.Лучшим/единственным заполнителем является пустой массив: запись &mut [] может быть &'c mut [X] для любого типа X и любого срока службы 'c, так как нет данных для хранения и поэтому ничто не требует инициализации, и никакие данные никогда не станут недействительными. В частности, это может быть &'b mut [T]:

fn pop_front<'a, 'b>(slice: &'a mut &'b mut [i32]) { 
    let value: &'b mut [i32] = mem::replace(slice, &mut []); 
    *slice = &mut value[1..] 
} 

(Как и выше, я сделал все более явным, чем это необходимо.)

+0

Большое вам спасибо за подробное объяснение и аннотирование жизненных циклов. Это невероятно полезно! – Cornstalks

3

Если вам необходимо изменить неизменяемый срез, see Cornstalks's answer.

Вы не можете модифицировать изменяемый кусочек в безопасном ржавчине. Когда вы берете подсечку измененного фрагмента, вы фактически заимствуете из исходного фрагмента. Это означает, что подошва не должна пережить оригинальный срез.

Вы хотите что-то, что выглядит следующим образом:

fn pop_front(slice: &mut &mut [i32]) { 
    *slice = &mut slice[1..]; 
} 

но subslice slice[1..] действует только до конца функции, и которые указывают Заимствование закончится, и первоначальный срез (параметр slice) будет снова можно использовать.

Мы можем использовать некоторые unsafe код для построения вручную срез мы хотим:

use std::slice; 

fn pop_front(slice: &mut &mut [i32]) { 
    let ptr = slice.as_mut_ptr(); 
    let len = slice.len(); 
    *slice = unsafe { slice::from_raw_parts_mut(ptr.offset(1), len - 1) }; 
} 

fn main() { 
    let mut slice = &mut [0, 1, 2, 3][..]; 
    pop_front(&mut slice); 
    println!("{:?}", slice); 
} 

playground

Эта программа выводит:

[1, 2, 3] 
3

Используя часть Francis Gagné's answer (я не сделал думаю о попытке &mut &), я смог заставить его работать без использования unsafe код:

fn pop_front(mut slice: &mut &[i32]) { 
    *slice = &slice[1..]; 
} 

fn main() { 
    let mut slice = &[0, 1, 2, 3][..]; 
    pop_front(&mut slice); 
    println!("{:?}", slice); 
} 

Output:

[1, 2, 3] 
+0

Это мутирует неизменный срез, а не изменяемый среза. Непонятно из вопроса, какова была необходимость OP ... –

+0

@ FrancisGagné: это то, что я пытался задать в моем вопросе, хотя, честно говоря, я использую изменчивые фрагменты в своем реальном коде, поэтому ваш ответ был очень полезно там. Мне было трудно пытаться разумно говорить о изменяемых неизменяемых срезах в моем вопросе, потому что «изменяемые неизменные» звуки бессмысленны (но, вероятно, это правильный термин). – Cornstalks

+0

Ваш 'pop_front' принимает изменчивую * ссылку * на неизменяемый * срез *. Мой 'pop_front' принимает изменяемую ссылку на изменяемый фрагмент. –