Proposal
Problem statement
Data structures often need to distinguish between keys and values. For example, for B trees, the standard library provides both BTreeSet and BTreeMap. The binary heap in the standard library only has BinaryHeap. In cases where a value needs to be carried, it is common to use a type like WithPriority<K, V> (see below for the definition). Being able to modify the value is useful, but BinaryHeap does not provide such a function. So it would be good to have an as_mut_slice function for this usage.
Motivating examples or use cases
#[derive(PartialEq, Eq, PartialOrd, Ord)]
struct WithPriority<K, V> {
priority: K,
#[ignore(PartialEq, Eq, PartialOrd, Ord)]
value: V
}
fn main() {
let mut heap = BinaryHeap::new();
heap.push(WithPriority { priority: 100, value: vec![6] });
heap.push(WithPriority { priority: 110, value: vec![7] });
heap.push(WithPriority { priority: 130, value: vec![44, 2] });
heap.push(WithPriority { priority: 99, value: vec![8] });
heap.push(WithPriority { priority: 121, value: vec![5] });
for WithPriority { value, .. } in heap.as_mut_slice() {
value.push(4);
}
}
Solution sketch
impl BinaryHeap<T, A>
where
A: Allocator,
{
// Returns a mutable slice of all values in the underlying vector, in arbitrary
// order.
//
/// # Safety
///
/// After all changes are completed, the slice must be a max-heap,
/// i.e. for all indices `0 < i < slice.len()`,
/// `slice[(i - 1) / 2] >= slice[i]`.
pub unsafe fn as_mut_slice(&mut self) -> &mut [T];
}
The safety of this function should be the same as BinaryHeap::from_raw_vec.
It looks strange that it's marked as unsafe while BinaryHeap::as_slice is marked as safe, but there is a similar case in the standard library: str::as_bytes_mut is unsafe while str::as_bytes is safe.
Alternatives
- Use other implementations of the binary heap.
- There are already so many implementations. It causes pains.
binary-heap-plus, provides MinComparator, MaxComparator, KeyComparator and FnComparator. It's a fork of BinaryHeap in the standard library.
dary_heap, provides TernaryHeap and QuaternaryHeap. It's a fork of BinaryHeap in the standard library.
heapify, provides C++ <algorithm> style heap operations. It's a little buggy.
min_max_heap, provides MinMaxHeap.
- Use
BinaryHeap::into_vec and BinaryHeap::from_raw_vec for modifiying elements.
- It may cause borrow-checking troubles.
- Use interior mutability.
- It may introduce overhead unless the mutable container is
Cell.
- If the mutable container is not thread-safe, e.g.
Cell, it prevents modifying the slice in parallel.
- Use
BTreeMap.
BTreeMap usually cannot replace BinaryHeap because BTreeMap does not allow duplicate keys.
- It is often more ineffective.
Links and related work
BinaryHeap::as_slice was stablized in Rust 1.80.
BinaryHeap::from_raw_vec is nightly-only experimental API.
This ACP proposes BinaryHeap::iter_mut at the first. There has previously been a attempt about adding BinaryHeap::iter_mut in rust-lang/rust#98524. The function proposed in that issue rebuilds the heap when the iterator is dropped. Unlike the function suggested in that issue, this function does not rebuild the heap.
What happens now?
This issue contains an API change proposal (or ACP) and is part of the libs-api team feature lifecycle. Once this issue is filed, the libs-api team will review open proposals as capability becomes available. Current response times do not have a clear estimate, but may be up to several months.
Possible responses
The libs team may respond in various different ways. First, the team will consider the problem (this doesn't require any concrete solution or alternatives to have been proposed):
- We think this problem seems worth solving, and the standard library might be the right place to solve it.
- We think that this probably doesn't belong in the standard library.
Second, if there's a concrete solution:
- We think this specific solution looks roughly right, approved, you or someone else should implement this. (Further review will still happen on the subsequent implementation PR.)
- We're not sure this is the right solution, and the alternatives or other materials don't give us enough information to be sure about that. Here are some questions we have that aren't answered, or rough ideas about alternatives we'd want to see discussed.
Proposal
Problem statement
Data structures often need to distinguish between keys and values. For example, for B trees, the standard library provides both
BTreeSetandBTreeMap. The binary heap in the standard library only hasBinaryHeap. In cases where a value needs to be carried, it is common to use a type likeWithPriority<K, V>(see below for the definition). Being able to modify the value is useful, butBinaryHeapdoes not provide such a function. So it would be good to have anas_mut_slicefunction for this usage.Motivating examples or use cases
Solution sketch
The safety of this function should be the same as
BinaryHeap::from_raw_vec.It looks strange that it's marked as
unsafewhileBinaryHeap::as_sliceis marked assafe, but there is a similar case in the standard library:str::as_bytes_mutis unsafe whilestr::as_bytesis safe.Alternatives
binary-heap-plus, providesMinComparator,MaxComparator,KeyComparatorandFnComparator. It's a fork ofBinaryHeapin the standard library.dary_heap, providesTernaryHeapandQuaternaryHeap. It's a fork ofBinaryHeapin the standard library.heapify, provides C++<algorithm>style heap operations. It's a little buggy.min_max_heap, providesMinMaxHeap.BinaryHeap::into_vecandBinaryHeap::from_raw_vecfor modifiying elements.Cell.Cell, it prevents modifying the slice in parallel.BTreeMap.BTreeMapusually cannot replaceBinaryHeapbecauseBTreeMapdoes not allow duplicate keys.Links and related work
BinaryHeap::as_slicewas stablized in Rust 1.80.BinaryHeap::from_raw_vecis nightly-only experimental API.This ACP proposes
BinaryHeap::iter_mutat the first. There has previously been a attempt about addingBinaryHeap::iter_mutin rust-lang/rust#98524. The function proposed in that issue rebuilds the heap when the iterator is dropped. Unlike the function suggested in that issue, this function does not rebuild the heap.What happens now?
This issue contains an API change proposal (or ACP) and is part of the libs-api team feature lifecycle. Once this issue is filed, the libs-api team will review open proposals as capability becomes available. Current response times do not have a clear estimate, but may be up to several months.
Possible responses
The libs team may respond in various different ways. First, the team will consider the problem (this doesn't require any concrete solution or alternatives to have been proposed):
Second, if there's a concrete solution: