hyperium / http

Rust HTTP types
Apache License 2.0
1.15k stars 285 forks source link

`PathAndQuery` implements `std::hash::Hash` #582

Closed cratelyn closed 1 year ago

cratelyn commented 1 year ago

this provides an implementation of Hash for the PathAndQuery structure.

an example program is shown below, demonstrating why this would be a desirable improvement in ergonomics.

rust playground: https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=cb5e9eb3afc705d7760af0fe695fe3a5

✨ **See code snippet..** ✨ ```rust //! A small example program demonstrating the ergonomics of [`Hash::hash`]ing // structures that contain a [`http::uri::PathAndQuery`] in an inner field. #![allow(dead_code)] use { http::uri::PathAndQuery, std::hash::{Hash, Hasher}, }; /// A structure that *can* be hashed. /// /// Note that it must convert the [`PathAndQuery`] to a string in order to be /// derivably [`Hash`]able. #[derive(Hash)] struct CanBeHashed { inner: String, } impl CanBeHashed { pub fn new(path_and_query: PathAndQuery) -> Self { let inner = path_and_query.as_str().to_owned(); Self { inner } } pub fn path_and_query(&self) -> PathAndQuery { // We can derive a `Hash` implementation, but in order to access the // path and query, we have to parse the data again. self .inner .parse::() .expect("inner data is a valid `PathAndQuery`") } } /// A structure that *cannot* be derivably hashed. /// /// If we uncomment the derivation below, and comment out the manual /// implementation provided later, we will see the following error: /// /// ```ignore /// error[E0277]: the trait bound `PathAndQuery: Hash` is not satisfied /// --> src/main.rs:26:5 /// | /// 24 | #[derive(Hash)] /// | ---- in this derive macro expansion /// 25 | struct CannotBeHashed { /// 26 | inner: PathAndQuery, /// | ^^^^^^^^^^^^^^^^^^^ the trait `Hash` is not implemented for `PathAndQuery` /// ``` // #[derive(Hash)] struct CannotBeHashed { inner: PathAndQuery, } impl CannotBeHashed { fn new(inner: PathAndQuery) -> Self { Self { inner } } pub fn path_and_query(&self) -> &PathAndQuery { // The path and query can be cheaply accessed as such... &self.inner } } impl Hash for CannotBeHashed { fn hash(&self, state: &mut H) { // ...but we must manually implement `Hash`, casting the `PathAndQuery` // into a string slice. let Self { inner } = self; inner.as_str().hash(state); } } // NB: a clever reader may note `PathAndQuery::from_maybe_shared`, which could // reduce copying overhead! This still entails iterating through the buffer // to find the beginning of the query component, unfortunately. :,( fn main() {} ```