programowanieDeweloper bibliotek / Rust Library Developer

Jak w Rust wprowadza się kontrolę dostępu do pól struktur i jak jest to związane z widocznością modułów? Jak prawidłowo zorganizować publiczne interfejsy API, aby zminimalizować błędy podczas korzystania ze struktur poza modułem?

Zdaj rozmowy kwalifikacyjne z asystentem AI Hintsage

Odpowiedź.

Historia pytania:

W językach takich jak C++ czy Java modyfikatory dostępu (public, private, protected) zapewniają widoczność członków klasy, ale raczej elastycznie — i często nie zapobiegają błędnemu używaniu API. W Rust, począwszy od wczesnych wersji, stworzono system kontroli dostępu rygorystyczny i wyraźnie modułowy, aby ograniczyć "przenikanie" wewnętrznych szczegółów na zewnątrz.

Problem:

Strukturę często trzeba częściowo ukryć przed kodem zewnętrznym: na przykład pola są prywatne, a na zewnątrz wystawiane są tylko metody. Jeśli nie ograniczymy dostępu, można niezamierzenie uszkodzić inwarianty struktury (na przykład, bezpośrednio wystawiając wewnętrzny Vec). Nieuważne publikowanie struktury czyni API kruchym.

Rozwiązanie:

W Rust wszystko jest prywatne domyślnie. Słowo kluczowe pub służy do wyraźnego eksportu: struktury można osobno ogłaszać jako widoczne, a pola — jako ukryte. Metody ogłasza się jako pub lub prywatne indywidualnie. Ponadto niestandardowe formy, takie jak pub(crate) lub pub(super), pozwalają na precyzyjne dostosowanie poziomu dostępu.

Przykład kodu:

mod domain { pub struct User { pub name: String, age: u32, // prywatne pole } impl User { pub fn new(name: String, age: u32) -> Self { Self { name, age } } pub fn age(&self) -> u32 { self.age } } } use domain::User; fn main() { let u = User::new("Eve".to_string(), 24); println!("{} {}", u.name, u.age()); // u.age — błąd dostępu! pole zamknięte poza modułem }

Kluczowe cechy:

  • Domyślnie wszystko jest prywatne, w tym pola i funkcje
  • pub wystawia tylko wyraźnie wybrane elementy na zewnątrz
  • pub(crate), pub(super) dają elastyczne dostosowanie dostępu dla dużych projektów

Pytania z podstępem.

Czy można zrobić strukturę pub, ale wszystkie jej pola pozostawić prywatnymi? Jak wtedy tworzyć jej instancje poza modułem?

Tak. Zwykle tak się robi: struktura pub, pola prywatne, tworzenie tylko przez publiczne konstruktory (na przykład, pub fn new...).

Czy pole struktury będzie widoczne w kodzie zewnętrznym przy ogłoszeniu pub struct Foo?

Nie, domyślnie każde pole wciąż jest prywatne — należy wyraźnie zdefiniować pub dla pola. pub struct — tylko czyni typ widocznym.

Czy pub działa dla enum w ten sam sposób?

W przypadku enum pub rozciąga się na wszystkie warianty, ale dla danych powiązanych w wariantach (na przykład, pole wewnątrz Variant(value: T)) również trzeba wyraźnie wskazać pub, jeśli chce się udostępnić wewnętrzności.

Typowe błędy i antywzorce

  • Uczynienie wszystkich pól pub dla prostoty, naruszając enkapsulację
  • Próba bezpośredniego dostępu do prywatnych pól z zewnętrznych modułów (błąd kompilacji)
  • Zapominanie o zrobieniu publicznej metody do konstruowania/modyfikacji struktury, jeśli wszystkie pola są zamknięte

Przykład z życia

Negatywny przypadek

W bibliotece struktura została ogłoszona jako pub struct Config, wszystkie pola również publiczne — aby użytkownik "widział" je. W efekcie każdy zewnętrzny kod mógł dowolnie zmieniać stan, naruszając inwarianty, co prowadziło do panic w najmniej oczekiwanym momencie.

Zalety:

  • Maksymalna otwartość i elastyczność dla użytkownika

Wady:

  • Naruszenie enkapsulacji
  • Trudności z dalszą migracją i wersjonowaniem API
  • Wzrost błędów z powodu nieprawidłowego użycia pól

Pozytywny przypadek

Struktura Config jest publiczna, wszystkie pola są prywatne. Ustawienia — tylko przez metody budownicze, domyślny konstruktor lub funkcje setter/getter. Poza modułem nie można zepsuć inwariantów.

Zalety:

  • API jest czyste, inwarianty pod kontrolą
  • Łatwiej utrzymać zgodność z poprzednimi wersjami

Wady:

  • Dla złożonych struktur — więcej kodu (metody, konstruktory, testy)