Thanks to visit codestin.com
Credit goes to github.com

Skip to content

ACP: BinaryHeap::as_mut_slice #756

@usamoi

Description

@usamoi

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

  1. 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.
  2. Use BinaryHeap::into_vec and BinaryHeap::from_raw_vec for modifiying elements.
    • It may cause borrow-checking troubles.
  3. 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.
  4. 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.

Metadata

Metadata

Assignees

No one assigned

    Labels

    ACP-acceptedAPI Change Proposal is accepted (seconded with no objections)T-libs-apiapi-change-proposalA proposal to add or alter unstable APIs in the standard libraries

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions