Skip to content

Commit f0ba97f

Browse files
author
katelyn martin
authored
implement Hash for PathAndQuery (#582)
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 ```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::<PathAndQuery>() .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<H: Hasher>(&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() {} ```
1 parent 34dc1cc commit f0ba97f

File tree

1 file changed

+7
-1
lines changed

1 file changed

+7
-1
lines changed

src/uri/path.rs

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
use std::convert::TryFrom;
22
use std::str::FromStr;
3-
use std::{cmp, fmt, str};
3+
use std::{cmp, fmt, hash, str};
44

55
use bytes::Bytes;
66

@@ -342,6 +342,12 @@ impl fmt::Display for PathAndQuery {
342342
}
343343
}
344344

345+
impl hash::Hash for PathAndQuery {
346+
fn hash<H: hash::Hasher>(&self, state: &mut H) {
347+
self.data.hash(state);
348+
}
349+
}
350+
345351
// ===== PartialEq / PartialOrd =====
346352

347353
impl PartialEq for PathAndQuery {

0 commit comments

Comments
 (0)