All Algorithms Implemented in Rust
github.com
github.com
Surely the best way is to sort the numbers and then walk from both ends?
Nice work anyway!
pub fn two_sum(nums: Vec<i32>, target: i32) -> Option<(usize, usize)> {
let mut hash_map: HashMap<i32, i32> = HashMap::new();
for (i, item) in nums.iter().enumerate() {
match hash_map.get(&(target - item)) {
Some(value) => return Some((i, *value)),
None => hash_map.insert(*item, i);
}
}
None
}
If the caller wants to do something weird like converting the index to an i32 (which immediately means you can’t handle 2³¹ or more (~2 billion) items) or turning it into a zero-or-two-element Vec (which is inefficient and far easier to make mistakes with), they can do that themselves.This speaks very clearly to me that either there is some unreasonable constraint in place that I don’t immediately see, or neither the author nor the reviewer was at all competent in Rust.
Sorting an array is O(n*log(n))
Hashmap operations are constant time, and walking through array to put stuff in it is O(n)
So, implementation based on Hashmap should be slightly faster. It requires extra memory, though.
It’s also going to explode if I put in a number with several thousand digits as it is unwrap()ing everywhere
https://github.com/TheAlgorithms/Rust/search?q=unsafe
Almost there...
It feels like a very early attempt. Skip the parent pointers and use Option rather than raw null pointers. Should get rid of the unsafe.
But with a doubly linked list, who owns the memory to each node?
In reality, you can do a doubly linked list with something like `RefCell`.
See the famous blog post on linked lists in rust here: https://rust-unofficial.github.io/too-many-lists/fourth-buil...
They're almost always a bad data structure to pick though, as the speed difference between being able to iterate a contiguous list, and even completely rebuild it, vs having to follow arbitrary pointers mean that the 'complexity' improvements of a linked list often become irrelevant.
------ Open Question)
I have this intuition that the borrow checker might enforce a way of working that fundamentally stops you from doing things that aren't _really_ compatible with the way a CPU actually works on the lowest level.
Is there any credence to that, and if so, is it happenstance, or is there some more fundamental reason?
No, I think you are reading too much into it. One is free to Box-allocate/pointer-chase on every line in Rust without any trouble. Sure, as other low level languages, rust also prefers/makes stack-allocation more ergonomic/the default, which is a quite cache-friendly way of operation but not even the stack is “native” in and of itself - it is just a frequently and extensively used part of the “heap”.
For the few times they are useful (e.g. if you're writing lock-free algorithms, you want a hard upper bound O(1) append rather than just amortized O(1) append, or you don't have malloc available), Rust is a fine language to write them in. But also there are plenty of high-quality libraries on crates.io, so you probably won't have to write your own.
Those guarantees go away if you use "unsafe". In that case, you may just use C or C++.
Also - many of the borrow checker’s guarantees are enforced inside unsafe as well.