2016-06-17 12 views
6

Poniższy kod kompiluje i działa poprawnie:Dlaczego Rust nie rozpoznaje, że zmienną jest i str?

use std::fmt::Display; 

fn display(x: &str) { 
    println!("{}", x); 
} 

fn main() { 
    let s: &str = "hi there"; 
    display(s); 
} 

Jeśli jednak zmienić funkcję display być

fn display(x: &Display) 

To daje następujący błąd:

src/main.rs:9:13: 9:14 error: the trait `core::marker::Sized` is not implemented for the type `str` [E0277] 
src/main.rs:9  display(s); 

Zmieniając display(s) do display(&s) znowu działa.

Co się tutaj dzieje? Oczywiście typ to &str, ale gdy &Display jest argumentem wejściowym, nie rozpoznaje tego.

Uwaga: &34 również działa jako argument w porządku. Czy to dlatego, że Display jest faktycznie zaimplementowany dla &str, a nie str?

Odpowiedz

6

Żądasz że &str powinien być zmuszany do &Display (odniesienie do trait obiektu), co wydaje się mieć sens, gdyż typ str implementuje Display.

Jeszcze jako Rust 1,9 (i nie obecnych planach zmianę tego), konwersja do obiektu cecha jest możliwe tylko dla &T do &Trait jeśli typ T jest „Sized”.

Powodem jest wdrożenie.Obiekt cechy, taki jak &Display, składa się z dwóch pól, jednego wskaźnika do danych i jednego wskaźnika do tabeli metod cech (vtable). Ta reprezentacja jest możliwa tylko dla wartości, których odniesienia są "cienkie", które są dokładnie typu where T: Sized. A &str jest referencją "grubą", ma wskaźnik i długość, a więc str nie może być danymi obiektu cechy.


Why does display(&s) work though? I guess that is a referene to the "fat" reference?

Tak, dokładnie. A &&str, jest "cienką" referencją, która wskazuje na zmienną o wartości &str. Więc można go przekonwertować na &Display.

+1

Dlaczego 'display (& s)' działa? Sądzę, że to jest odwołanie do "grubego" odniesienia? Czy są inne typy, które są "grubymi" recenzentami, czy też są w jakiś sposób wyjątkowe? – vitiral

+1

Chyba nie rozumiem, dlaczego 'str' nie jest po prostu strukturą z danymi i długością, zamiast' & str'? Wtedy '& str' nie byłby już grubszym wskaźnikiem i nie mielibyśmy tego problemu! – vitiral

+1

Poszukałem grubych wskazówek i to był całkiem dobry odnośnik: https://www.reddit.com/r/rust/comments/38tv6n/when_is_a_reference_not_a_pointer/ Będę badać ... – vitiral

5

(Promowane moje edycje głównej odpowiedź)

Kiedy piszesz fn display(x: &Display), funkcja przyjmuje wartość, która może być zmuszany do trait object przez dereferencji go. Również funkcja Rust wymaga, aby rozmiar wartości parametru x był znany w czasie kompilacji.

Po typie &34 (&u32) jest dereferencjonowany i wymuszany na obiekt cechy, staje się u32 i można określić jego rozmiar.

Po dereferencji &str staje się str, a jego rozmiar nie może zostać określony, ponieważ długość łańcucha może być dowolna.

Dodając & do &str (&&str) jest dereferencjonowane powrotem do &str, co jest wskaźnikiem, a jej wielkość może być ustalona. Uważam, że właśnie dlatego kompilator akceptuje tylko kod display(&s).

+0

To ... To się dzieje. Może promujesz swoją edycję w głównej odpowiedzi? –

+0

Dziękuję za sugestię. Promowałem moją edycję (z kilkoma dodatkowymi zmianami dla jasności) w głównej odpowiedzi. –

+0

Książka mówi: "Cecha jest bezpieczna dla obiektu, jeśli obie te rzeczy są prawdziwe: 1) cecha nie wymaga, że" Self: Sized ", 2) wszystkie jej metody są bezpieczne dla obiektów". Jednak tylko odwołanie do wartości "wielkości" można odrzucić do obiektu cechy – aSpex

1

By changing display(s) to display(&s) it works again.

To wszystko idzie w dół do &str: Display (+ Sized) i str: !Display (jest to tylko zapis)

  • display(s) spodziewa s: &Display, =>&str: &Display które są fałszywe.
  • display(&s) spodziewa &s: &Display =>&(&str) : &Display co jest prawdą, jak &str: Display
+0

'Display' implementowany dla' str', a nie dla '& str'. http://doc.rust-lang.org/src/core/up/src/libcore/fmt/mod.rs.html#1371-1375 – aSpex

Powiązane problemy