2015-12-20 11 views
5

Parametry mogą być przekazywane do funkcji i modyfikacji:Jak zmodyfikować plaster, który jest parametrem funkcji?

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

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

Output:

42 

Naiwnie zmiany kodu użyć kawałek nie z całą masę błędów:

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 

Jeśli spróbujemy stosując zmienny slice (co nie jest to, co naprawdę chcę; Nie chcę, aby zmodyfikować wartości w kawałkach, po prostu chcę, aby zmodyfikować sam kawałek więc obejmuje mniejszy zakres elementów) oraz zmienny parametr, to nie ma wpływu na oryginalny kawałek:

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] 

Pytanie: Czy istnieje sposób zmodyfikować kawałek, który jest parametrem funkcji? Nie chcę modyfikować elementów wewnątrz plasterka; Chcę tylko zmodyfikować zakres samego plasterka, aby stał się mniejszym "pod-plasterkiem".

Odpowiedz

5

Jak mówili inni, rdzeń Chodzi o to, aby wziąć &mut &... [T] (gdzie ... jest mut lub pusta) i odczytu/zapisu do wewnętrznej plaster. Inne odpowiedzi pokazują, że jest możliwe &mut &[T] w bezpiecznym kodzie i możliwe, że dla &mut &mut [T] z niebezpiecznym kodem, ale nie wyjaśniają, dlaczego istnieje różnica ... i &mut &mut [T] jest również możliwe z bezpiecznym kodem.

W warunkach wyraźnego-lifetime, zagnieżdżone odniesienia jest coś &'a mut &'b ... [T] dla niektórych wcieleń 'a i 'b, a celem jest, aby uzyskać &'b ... [T], pokroić go i napisać, że w &'a mut.

Dla &'a mut &'b [T], to jest proste: &[T] jest kopia, więc pisanie *slice = &slice[1..] skutecznie skopiować &'b [T] z &mut a następnie, później zastąpić istniejącą wartość z krótsza. Kopia oznacza, że ​​jeden dosłownie dostaje &'b [T], z którym można operować, więc nie ma bezpośredniego połączenia między nim a &'a mut, a zatem legalne jest mutowanie. Jest coś takiego jak skutecznie

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

(mam oznakowane z wcieleń i uwagami typu związać do mojego wyjaśnienia, ale nie jest to wymagane przez kodeks pracy).

Dla &'a mut &'b mut [T] rzeczy są trochę trudniejsze: &mut [T] nie mogą być kopiowane: dereferencing nie skopiuje, to reborrow uzyskując &'a mut [T] czyli wycinka ma żywotność, która jest podłączona do zewnętrzna&'a mut, a nie wewnętrzna &'b mut [T]. Oznacza to, że pokrojone odwołanie ma krótszy czas życia niż typ, który próbuje nadpisać, więc nieważne jest przechowywanie wycinka w tej pozycji. Innymi słowy:

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

Sposób to zrobić bezpiecznie dla &'a mut &'b mut [T] jest uzyskanie wewnętrznego kawałek z odniesieniem z tym 'b życia. Wymaga to śledzenia reguły "jednego właściciela", braku pożyczek, a najlepszą funkcją tego rodzaju manipulacji właścicielami jest mem::replace. Pozwala nam wyodrębnić wewnętrzną warstwę, zamieniając ją na jakiś element zastępczy, który możemy następnie zastąpić krótką wersją.Najlepszym/jedynym symbolem zastępczym jest pusta tablica: pisanie &mut [] może być &'c mut [X] dla dowolnego typu X i każdego okresu użytkowania 'c, ponieważ nie ma danych do zapisania, więc nic nie wymaga inicjalizacji, a żadne dane nie staną się nieważne. W szczególności może to być &'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..] 
} 

(Jak wyżej, zrobiłem to bardziej wyraźne niż to konieczne.)

+0

Dziękuję bardzo za szczegółowe wyjaśnienie i adnotację o życiu. Jest niesamowicie pomocny! – Cornstalks

3

Jeśli musisz zmodyfikować niezmienny plaster, see Cornstalks's answer.

Nie można modyfikować wycinków w bezpiecznym rdzeniu. Kiedy weźmiesz subslice z wycinanym plasterkiem, efektywnie pożyczasz z oryginalnego plasterka. Oznacza to, że podprzestrzeń nie może przeżyć oryginalnego plasterka.

chcesz coś, co wygląda tak:

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

ale subslice slice[1..] jest ważna tylko do końca tej funkcji, a tym momencie pożyczyć skończy i oryginalny slice (parametr slice) będzie znów być użyteczny.

Możemy użyć kodu unsafe skonstruować ręcznie plasterek chcemy:

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

Ten program wyjścia:

[1, 2, 3] 
3

wykorzystując część Francis Gagné's answer (nie myśl o próbowaniu &mut &), udało mi się uruchomić go bez użycia kodu 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

Ten mutuje się niezmienną kawałek, a nie zmienny plaster. Z tego, co było potrzebne OP, nie wynika jednoznacznie ... –

+0

@ FrancisGagné: o to właśnie pytałem w pytaniu, ale szczerze mówiąc, używam wycinków w moim prawdziwym kodzie, więc twoja odpowiedź była bardzo pomocny tam. Ciężko mi było próbować rozsądnie mówić o zmiennych, niezmiennych fragmentach mojego pytania, ponieważ "mutable immutable" brzmi nonsensownie (ale prawdopodobnie jest to poprawne określenie). – Cornstalks

+0

Twoje 'pop_front' przyjmuje zmienną * referencję * do niezmiennego * plasterka *. Moje 'pop_front' przyjmuje zmienne odniesienie do zmiennego plasterka. –

Powiązane problemy