Borrow und AsRef ermöglichen beide die Umwandlung von Referenzen zu Referenzen, aber Borrow stellt strenge vertragliche Garantien für Gleichheits- und Hashingsemantiken zur Verfügung. Wenn ein Typ Borrow<T> implementiert, verspricht er, dass borrow() einen Wert zurückgibt, der gleich (über Eq) vergleicht und identische Hashwerte (über Hash) zum ursprünglichen Typ produziert. AsRef fehlen diese Einschränkungen; es erlaubt lediglich eine kostengünstige Umwandlung, ohne dass das konvertierte Objekt das gleiche Hash- oder Gleichheitsverhalten aufrechterhält. HashMap benötigt Borrow für seine get-Methode, weil sichergestellt werden muss, dass ein als String eingefügter Schlüssel zuverlässig mit einem &str abgerufen werden kann, was garantiert, dass beide Typen auf denselben internen Bucket abgebildet werden und bei der Konfliktlösung gleich vergleichen.
Sie entwerfen eine Hochleistungs-Routing-Tabelle für einen HTTP-Proxy, bei der Routenschlüssel als eigene String-Objekte gespeichert werden, aber eingehende Anfragen Pfadsegmente als &str-Schnitte aus dem Netzwerkpuffer bereitstellen.
Sie bewerten drei Implementierungsstrategien. Zuerst könnten Sie alle Schlüssel bei der Abfrage in String normalisieren, um Typenuniformität zu gewährleisten; dies erweist sich jedoch aufgrund der Allokationen bei jeder Anfrage als übermäßig teuer. Zweitens ziehen Sie in Betracht, AsRef<str> als Abrufgrenze zu verwenden, was sowohl String als auch &str zu &str konvertieren erlaubt; jedoch erlaubt AsRef Implementierungen, bei denen die referenzierten Daten unterschiedliche Unicode-Normalisierungsformen oder Kodierungen verwenden könnten, was dazu führt, dass "café" als String und "café" als &str auf unterschiedliche Buckets hashen und Cache-Verluste trotz logischer Gleichheit verursachen. Drittens übernehmen Sie Borrow<str>, das vertraglich garantiert, dass String::borrow() und &str identische Hash- und Eq-Ergebnisse produzieren, wodurch ein konsistentes Bucket-Indexing gewährleistet ist.
Sie wählen den Ansatz Borrow, da er die Allokationen pro Anfrage eliminiert und gleichzeitig die Hash-Konsistenz für eine korrekte Routenauflösung aufrechterhält. Das Ergebnis ist ein Null-Kopien-Abfragemechanismus, der &str aus dem Netzwerk akzeptiert und korrekt mit vorab eingefügten String-Routen übereinstimmt, ohne dass es zu semantischer Drift kommt.
Warum führen die Verwendung von AsRef<str> anstelle von Borrow<str> für HashMap-Abrufe zu subtilen Abruffehlern?
Während AsRef<str> das Konvertieren sowohl von String als auch von &str zu &str erlaubt, bietet es keine Garantie dafür, dass der Hash der konvertierten Referenz mit dem Hash des ursprünglichen Eigentumswertes übereinstimmt. HashMap verwendet den Hashwert, um den Speicherbucket zu bestimmen; wenn String und &str für denselben logischen Inhalt unterschiedliche Hashwerte erzeugen (zum Beispiel aufgrund unterschiedlicher interner Darstellungen oder Normalisierungen), würde die Abfrage den falschen Bucket durchsuchen und None zurückgeben, obwohl der Schlüssel existiert. Borrow verhindert dies, indem es verlangt, dass hash(x.borrow()) == hash(x) und x.borrow() == x, was sicherstellt, dass heterogene Typen immer auf identische Speicherorte abgebildet werden, wenn sie logisch gleich sind.
Warum benötigt HashMap sowohl Borrow als auch Eq/Hash Trait-Grenzen gleichzeitig, obwohl Borrow bereits Gleichheitssemantiken impliziert?
Borrow etabliert die Äquivalenz zwischen den eigenen und den ausgeliehenen Darstellungen, aber Eq und Hash definieren die tatsächlichen Vergleichs- und Hashalgorithmen. Borrow garantiert nur, dass diese Algorithmen über den Typgrenzen hinweg konsistente Ergebnisse liefern; die Implementierung selbst erfolgt nicht. HashMap benötigt Eq, um Kollisionen innerhalb eines Buckets aufzulösen, und Hash, um den Bucket-Index anfänglich zu berechnen. Ohne Borrow könnte die Map &str nicht sicher akzeptieren, um einen String-Schlüssel zu finden; ohne Eq und Hash könnte sie keine erforderlichen Vergleiche durchführen oder den Hashwert überhaupt berechnen.
Wie unterscheidet sich Borrow von Deref, wenn durch Smart-Pointer-Projektionen projiziert wird, und warum verlässt sich HashMap auf Borrow für String-Schlüssel anstelle von Deref-Zwang?
Deref bietet eine automatische Zwangsumwandlung zu einem Zieltyp und impliziert eine Referenzbeziehung, verlangt jedoch keine Konsistenz von Hash oder Gleichheit zwischen dem Smart Pointer und seinem Ziel. Ein hypothetischer Smart Pointer könnte Deref zu einer zwischengespeicherten oder transformierten Ansicht, die sich von der internen Darstellung zum Hashen unterscheidet. Borrow verlangt explizit, dass die ausgeliehene Ansicht identische Hash- und Eq-Semantiken wie das Original aufrechterhält. HashMap verwendet Borrow für String, weil sichergestellt werden muss, dass "key" (als String) und "key" (als &str) im selben Bucket kollidieren; allein der Zwang von Deref fehlt die formale Garantie, dass String::deref() und &str denselben Hash-Implementierungsvertrag teilen, während Borrow diese Invarianz kodifiziert.