I'm working through some problems on leetcode to get better at using Rust during interviews. As a first attempt to solve this problem, I thought of representing triplet solutions a + b + c = 0
by storing a
, b
, and c
in a solution: HashSet<i32>
, then storing that solution: HashSet<i32>
in another collection solution_set: HashSet<HashSet<i32>>
. Crazy, right?
The exercise explicitly states that redundant triplets don't qualify, so rather than storing the triplets in solution: Vec<i32>
s where order might change a Vec
's hash value, I thought I'd store the triplets in solution: HashSet<i32>
so any ordering of a
, b
, and c
resolves to the same solution
. Additionally, it would be O(1)
time to verify that a triplet already exists in solution_set: HashSet<HashSet<i32>>
, rather than O(n)
to check if it exists in the alternative solution_set: Vec<HashSet<i32>>
. Finally, I know the return value is Vec<Vec<i32>>
, but that's solved by drain()
ing the solution: HashSet<i32>
into Vec<i32>
, and then draining the resulting Iter<Vec<i32>>
into a Vec<Vec<i32>>
.
I recognize that HashSet<T>
does not implement Hash
, so I decided to try to myself, and now I'm up a paddle without a creek. I looked here to learn about implementing Hash
for a struct, and here to learn how to implement a trait on a struct I don't own, but now I'm re-implementing all the functions handles I need from HashSet
(new()
, drain()
, insert()
, etc) on HashSetWrapper
. The compiler is also compaining about other traits too like PartialEq
, so I've really opened pandora's box on this one. I just feel like this isn't the most "Rusty" way to do this.
Also, I know that implementing hashes correctly is not trivial, and as this is an effort in best practices I'd like some help figuring out the most "Rusty" way to implement my solution is. I haven't actually gotten it to work yet, but here's the code I have so far:
use std::collections::HashSet;
use std::hash::{Hash, Hasher};
#[derive(PartialEq)]
struct HashSetWrapper<T>(HashSet<T>);
impl<T: Hash> HashSetWrapper<T> {
fn new() -> Self {
HashSetWrapper(HashSet::<T>::new())
}
fn insert(&self, value: T) {
self.0.insert(value);
}
}
impl<T: Hash> Hash for HashSetWrapper<T> {
fn hash<H: Hasher>(&self, state: &mut H) {
for value in &self.0 {
value.hash(state);
}
}
}
impl Solution {
pub fn three_sum(nums: Vec<i32>) -> Vec<Vec<i32>> {
let mut solution_set: HashSetWrapper<HashSet<i32>> = HashSetWrapper::new();
for (i, a) in nums[0..(nums.len() - 2)].iter().enumerate() {
for (j, b) in nums[i..(nums.len() - 1)].iter().enumerate() {
for c in nums[j..].iter() {
if a + b + c == 0 {
let mut temp = HashSet::<i32>::new();
temp.insert(*a);
temp.insert(*b);
temp.insert(*c);
solution_set.insert(temp); }
}
}
}
solution_set.drain().map(|inner_set| inner_set.drain().collect::<Vec<_>>()).collect::<Vec<_>>()
}
}
I still need to implement a drain()
for my wrapper class, but I'm not even sure I'm going in the right direction. How would you solve this problem? How would you implement Hash
on HashSet
? I'd love to know!
Below are the errors the compiler is giving me:
Line 5, Char 26: binary operation `==` cannot be applied to type `std::collections::HashSet<T>` (solution.rs)
|
5 | struct HashSetWrapper<T>(HashSet<T>);
| ^^^^^^^^^^
|
= note: an implementation of `std::cmp::PartialEq` might be missing for `std::collections::HashSet<T>`
Line 5, Char 26: binary operation `!=` cannot be applied to type `std::collections::HashSet<T>` (solution.rs)
|
5 | struct HashSetWrapper<T>(HashSet<T>);
| ^^^^^^^^^^
|
= note: an implementation of `std::cmp::PartialEq` might be missing for `std::collections::HashSet<T>`
Line 9, Char 38: no function or associated item named `new` found for type `std::collections::HashSet<T>` in the current scope (solution.rs)
|
9 | HashSetWrapper(HashSet::<T>::new())
| ^^^ function or associated item not found in `std::collections::HashSet<T>`
|
= note: the method `new` exists but the following trait bounds were not satisfied:
`T : std::cmp::Eq`
Line 13, Char 16: no method named `insert` found for type `std::collections::HashSet<T>` in the current scope (solution.rs)
|
13 | self.0.insert(value);
| ^^^^^^ method not found in `std::collections::HashSet<T>`
|
= note: the method `insert` exists but the following trait bounds were not satisfied:
`T : std::cmp::Eq`
Line 28, Char 62: the trait bound `std::collections::HashSet<i32>: std::hash::Hash` is not satisfied (solution.rs)
|
8 | fn new() -> Self {
| ---------------- required by `HashSetWrapper::<T>::new`
...
28 | let mut solution_set: HashSetWrapper<HashSet<i32>> = HashSetWrapper::new();
| ^^^^^^^^^^^^^^^^^^^ the trait `std::hash::Hash` is not implemented for `std::collections::HashSet<i32>`
Line 38, Char 38: no method named `insert` found for type `HashSetWrapper<std::collections::HashSet<i32>>` in the current scope (solution.rs)
|
5 | struct HashSetWrapper<T>(HashSet<T>);
| ------------------------------------- method `insert` not found for this
...
38 | solution_set.insert(temp); }
| ^^^^^^ method not found in `HashSetWrapper<std::collections::HashSet<i32>>`
|
= note: the method `insert` exists but the following trait bounds were not satisfied:
`std::collections::HashSet<i32> : std::hash::Hash`
Line 42, Char 22: no method named `drain` found for type `HashSetWrapper<std::collections::HashSet<i32>>` in the current scope (solution.rs)
|
5 | struct HashSetWrapper<T>(HashSet<T>);
| ------------------------------------- method `drain` not found for this
...
42 | solution_set.drain().map(|inner_set| inner_set.drain().collect::<Vec<_>>()).collect::<Vec<_>>()
| ^^^^^ method not found in `HashSetWrapper<std::collections::HashSet<i32>>`
error: aborting due to 7 previous errors