Borrow i AsRef obie umożliwiają konwersję z referencji na referencję, ale Borrow narzuca ścisłe gwarancje dotyczące semantyki równości i haszowania. Gdy typ implementuje Borrow<T>, obiecuje, że borrow() zwraca wartość, która jest równoznaczna (poprzez Eq) i produkuje identyczne wartości haszujące (poprzez Hash) w stosunku do oryginalnego typu. AsRef nie ma tych ograniczeń; pozwala jedynie na tanią konwersję, nie wymagając, aby widok konwertowany zachował tę samą semantykę haszowania lub równości. HashMap wymaga Borrow dla swojej metody get, ponieważ musi zapewnić, że klucz wstawiony jako String można niezawodnie odzyskać za pomocą &str, gwarantując, że oba typy mapują do tego samego wewnętrznego wiadra i porównują się równo podczas rozwiązywania kolizji.
Projektujesz wysokowydajną tablicę routingu dla serwera proxy HTTP, gdzie klucze tras są przechowywane jako własne obiekty String, ale przychodzące żądania dostarczają segmenty ścieżek jako fragmenty &str analizowane z bufora sieciowego.
Oceniłeś trzy strategie implementacji. Po pierwsze, mógłbyś normalizować wszystkie klucze do String podczas wyszukiwania, zapewniając jednolitość typów; byłoby to jednak niewiarygodnie kosztowne z powodu alokacji na każde żądanie. Po drugie, rozważałeś użycie AsRef<str> jako ograniczenia wyszukiwania, co pozwoliłoby zarówno String, jak i &str na konwersję do &str; jednak AsRef dopuszcza implementacje, w których odwołane dane mogą używać różnych form normalizacji Unicode lub kodowań, powodując, że "café" jako String i "café" jako &str haszują się do różnych wiader i prowadzą do błędów w pamięci podręcznej pomimo semantycznej równości. Po trzecie, przyjąłeś Borrow<str>, które kontraktowo gwarantuje, że String::borrow() i &str produkują identyczne wyniki Hash i Eq, zapewniając spójne indeksowanie wiader.
Wybierasz podejście Borrow, ponieważ eliminuje ono alokacje na każde żądanie, jednocześnie zachowując spójność haszowania wymaganą do poprawnej rozwiązania tras. Rezultatem jest mechanizm wyszukiwania bez kopiowania, który przyjmuje &str z sieci i poprawnie dopasowuje wcześniej wstawione trasy String bez semantycznego dryfu.
Dlaczego użycie AsRef<str> zamiast Borrow<str> do wyszukiwania HashMap prowadzi do subtelnych błędów w odzyskiwaniu?
Podczas gdy AsRef<str> pozwala na konwersję zarówno String, jak i &str do &str, nie zapewnia żadnej gwarancji, że hasz konwertowanej referencji odpowiada haszowi oryginalnej własnej wartości. HashMap używa wartości haszowej do określenia wiadra magazynowego; jeśli String i &str produkują różne hasze dla tej samej logicznej treści (na przykład, z powodu różnych reprezentacji wewnętrznych lub normalizacji), wyszukiwanie będzie szukać w niewłaściwym wiadrze i zwróci None, mimo że klucz istnieje. Borrow zapobiega temu, wymagając, aby hash(x.borrow()) == hash(x) i x.borrow() == x, zapewniając, że heterogeniczne typy zawsze mapują do identycznych lokalizacji magazynowych, gdy są logicznie równe.
Dlaczego HashMap wymaga zarówno granic Borrow, jak i Eq/Hash, biorąc pod uwagę, że Borrow już implikuje semantykę równości?
Borrow ustala równoważność między reprezentacjami własnymi i pożyczonymi, ale Eq i Hash definiują faktyczne algorytmy porównania i haszowania. Borrow gwarantuje jedynie, że te algorytmy produkują spójne wyniki w całym zakresie typów; nie implementuje ich. HashMap potrzebuje Eq, aby rozwiązać kolizje w obrębie wiadra, i Hash, aby początkowo obliczyć indeks wiadra. Bez Borrow, mapa nie mogłaby bezpiecznie przyjąć &str, aby znaleźć klucz String; bez Eq i Hash nie mogłaby w ogóle przeprowadzać niezbędnych porównań ani obliczać wartości hasza.
Jak Borrow różni się od Deref podczas projektowania przez inteligentne wskaźniki i dlaczego HashMap polega na Borrow dla kluczy typu String zamiast na wymuszeniu Deref?
Deref zapewnia automatyczne wymuszenie do docelowego typu i implikuje relację referencyjną, ale nie wymusza spójności haszowania ani równości między inteligentnym wskaźnikiem a jego celem. Hipotetyczny inteligentny wskaźnik mógłby Deref do pamięci podręcznej lub przekształconego widoku, który różni się od reprezentacji wewnętrznej używanej do haszowania. Borrow wymaga wyraźnie, aby pożyczony widok zachowywał identyczne semantyki Hash i Eq wobec oryginału. HashMap używa Borrow dla String, ponieważ musi zapewnić, że "klucz" (jako String) i "klucz" (jako &str) kolidują w tym samym wiadrze; samodzielne wymuszenie Deref nie ma formalnej gwarancji, że String::deref() i &str dzielą ten sam kontrakt implementacji hasza, podczas gdy Borrow kodyfikuje ten inwariant.