Skip to content

Add FixedQueue: A Fixed Size Queue Implementation #126204

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 19 commits into from

Conversation

Alex-Velez
Copy link
Contributor

A new data structure, FixedQueue, which is a fixed-size queue implemented using a static ring buffer.

  • Both push and pop operations should be constant time.
  • Elements should be stored contiguously in memory.
  • Unit tests should cover all methods and functionality.

I recognize that Rust already has VecDeque, but it's growable and has a more general implementation. Please correct me if I'm wrong, but I believe this might offer a more efficient solution for scenarios where a fixed-size queue is sufficient.

I want to learn and am open to any suggestions or critiques.

Example Usage:

use std::collections::FixedQueue;

let mut fque = FixedQueue::<i32, 3>::new();
fque.push(1);
fque.push(2);
fque.push(3);
assert_eq!(fque.pop(), Some(1));
assert_eq!(fque.pop(), Some(2));
fque.push(4);
assert_eq!(fque.to_vec(), vec![3, 4]);

A new data structure, `FixedQueue`, which is a fixed-size queue implemented using a static ring buffer. 

* Both push and pop operations should be constant time.
* Elements should be stored contiguously in memory.
* Unit tests should cover all methods and functionality.

I recognize that Rust already has `VecDeque`, but it's growable and has a more general implementation. Please correct me if I'm wrong, but I believe this might offer a more efficient solution for scenarios where a fixed-size queue is sufficient.

I want to learn and am open to any suggestions or critiques.

Example Usage:
```rust
use std::collections::FixedQueue;

let mut fque = FixedQueue::<i32, 3>::new();
fque.push(1);
fque.push(2);
fque.push(3);
assert_eq!(fque.pop(), Some(1));
assert_eq!(fque.pop(), Some(2));
fque.push(4);
assert_eq!(fque.to_vec(), vec![3, 4]);
```
@rustbot
Copy link
Collaborator

rustbot commented Jun 9, 2024

r? @Nilstrieb

rustbot has assigned @Nilstrieb.
They will have a look at your PR within the next two weeks and either review your PR or reassign to another reviewer.

Use r? to explicitly pick a reviewer

@rustbot rustbot added S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. T-libs Relevant to the library team, which will review and decide on the PR/issue. labels Jun 9, 2024
@rust-log-analyzer

This comment has been minimized.

mingw-check-tidy formatting errors fix
@rust-log-analyzer

This comment has been minimized.

Fixing file formatting errors
Fixing file fomatting errors
insert tests
moved fixed queue into seperate folder
allow fixed queue to be a module
@rust-log-analyzer

This comment has been minimized.

add unstable feature
include vec and string
@rust-log-analyzer

This comment has been minimized.

@rust-log-analyzer

This comment has been minimized.

@rust-log-analyzer

This comment has been minimized.

unstable tags
@rust-log-analyzer

This comment has been minimized.

including docs
@rust-log-analyzer

This comment has been minimized.

add symbol
@rust-log-analyzer

This comment has been minimized.

sort by alphabetical order
@rust-log-analyzer

This comment has been minimized.

@rust-log-analyzer

This comment has been minimized.

std to alloc (perhaps wrong place)
@rust-log-analyzer

This comment has been minimized.

use feature in example
@rust-log-analyzer
Copy link
Collaborator

The job x86_64-gnu-llvm-17 failed! Check out the build log: (web) (plain)

Click to see the possible cause of the failure (guessed by this bot)
#16 exporting to docker image format
#16 sending tarball 29.3s done
#16 DONE 34.4s
##[endgroup]
Setting extra environment values for docker:  --env ENABLE_GCC_CODEGEN=1 --env GCC_EXEC_PREFIX=/usr/lib/gcc/
[CI_JOB_NAME=x86_64-gnu-llvm-17]
---
sccache: Starting the server...
##[group]Configure the build
configure: processing command line
configure: 
configure: build.configure-args := ['--build=x86_64-unknown-linux-gnu', '--llvm-root=/usr/lib/llvm-17', '--enable-llvm-link-shared', '--set', 'rust.thin-lto-import-instr-limit=10', '--set', 'change-id=99999999', '--enable-verbose-configure', '--enable-sccache', '--disable-manage-submodules', '--enable-locked-deps', '--enable-cargo-native-static', '--set', 'rust.codegen-units-std=1', '--set', 'dist.compression-profile=balanced', '--dist-compression-formats=xz', '--disable-dist-src', '--release-channel=nightly', '--enable-debug-assertions', '--enable-overflow-checks', '--enable-llvm-assertions', '--set', 'rust.verify-llvm-ir', '--set', 'rust.codegen-backends=llvm,cranelift,gcc', '--set', 'llvm.static-libstdcpp', '--enable-new-symbol-mangling']
configure: target.x86_64-unknown-linux-gnu.llvm-config := /usr/lib/llvm-17/bin/llvm-config
configure: llvm.link-shared     := True
configure: rust.thin-lto-import-instr-limit := 10
configure: change-id            := 99999999
---
normalized stderr:
error[E0433]: failed to resolve: use of undeclared type `FixedQueue`
##[error]  --> $DIR/feature-gate-fixed-queue.rs:6:18
   |
LL |     let _queue = FixedQueue::<i32, 3>::new();
   |                  ^^^^^^^^^^ use of undeclared type `FixedQueue`
error: aborting due to 1 previous error

For more information about this error, try `rustc --explain E0433`.




The actual stderr differed from the expected stderr.
Actual stderr saved to /checkout/obj/build/x86_64-unknown-linux-gnu/test/ui/feature-gates/feature-gate-fixed-queue/feature-gate-fixed-queue.stderr
To only update this specific test, also pass `--test-args feature-gates/feature-gate-fixed-queue.rs`

error: 1 errors occurred comparing output.
status: exit status: 1
status: exit status: 1
command: env -u RUSTC_LOG_COLOR RUSTC_ICE="0" RUST_BACKTRACE="short" "/checkout/obj/build/x86_64-unknown-linux-gnu/stage2/bin/rustc" "/checkout/tests/ui/feature-gates/feature-gate-fixed-queue.rs" "-Zthreads=1" "-Zsimulate-remapped-rust-src-base=/rustc/FAKE_PREFIX" "-Ztranslate-remapped-path-to-local-path=no" "-Z" "ignore-directory-in-diagnostics-source-blocks=/cargo" "-Z" "ignore-directory-in-diagnostics-source-blocks=/checkout/vendor" "--sysroot" "/checkout/obj/build/x86_64-unknown-linux-gnu/stage2" "--target=x86_64-unknown-linux-gnu" "--check-cfg" "cfg(FALSE)" "--error-format" "json" "--json" "future-incompat" "-Ccodegen-units=1" "-Zui-testing" "-Zdeduplicate-diagnostics=no" "-Zwrite-long-types-to-disk=no" "-Cstrip=debuginfo" "--emit" "metadata" "-C" "prefer-dynamic" "--out-dir" "/checkout/obj/build/x86_64-unknown-linux-gnu/test/ui/feature-gates/feature-gate-fixed-queue" "-A" "unused" "-A" "internal_features" "-Crpath" "-Cdebuginfo=0" "-Lnative=/checkout/obj/build/x86_64-unknown-linux-gnu/native/rust-test-helpers" "-L" "/checkout/obj/build/x86_64-unknown-linux-gnu/test/ui/feature-gates/feature-gate-fixed-queue/auxiliary"
--- stderr -------------------------------
error[E0433]: failed to resolve: use of undeclared type `FixedQueue`
##[error]  --> /checkout/tests/ui/feature-gates/feature-gate-fixed-queue.rs:6:18
   |
   |
LL |     let _queue = FixedQueue::<i32, 3>::new(); //~ ERROR the type `FixedQueue` is unstable
   |                  ^^^^^^^^^^ use of undeclared type `FixedQueue`
error: aborting due to 1 previous error

For more information about this error, try `rustc --explain E0433`.
------------------------------------------

@workingjubilee workingjubilee added T-libs-api Relevant to the library API team, which will review and decide on the PR/issue. and removed T-libs Relevant to the library team, which will review and decide on the PR/issue. labels Jun 10, 2024
Copy link
Member

@workingjubilee workingjubilee left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see you like AI, and also have submitted some unusual Rust code that is... unidiomatic, especially considering the surrounding standard library idioms, as I hope my review comments illuminate. It more closely resembles C or JavaScript code I have read before. Before I kick this over to T-libs-api, I feel duty-bound to simply ask: Did you use an LLM to generate this code?

Comment on lines +94 to +96
for i in 0..N {
drop(self.buffer[i].take());
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if N is large, this attempts to run drop N times, even if the amount of elements actually in the queue is 1 or even 0, which seems...

...computationally wasteful, even with branch predictors being real. It could thrash the entire cache, for instance.

/// [`pop`]: FixedQueue::pop
#[derive(Debug)]
#[unstable(feature = "fixed_queue", issue = "126204")]
pub struct FixedQueue<T, const N: usize> {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Have you considered an implementation where N is not a constant, but is runtime-determined, yet immutable?

where
Option<T>: Copy,
{
self.buffer = [Some(item); N];
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I believe this semantically constructs the entire array as a temporary, then writes it. In the past, this sort of thing has led to unfortunate amounts of memcpy on the stack. I think it would be better to do

Suggested change
self.buffer = [Some(item); N];
for element in self.iter_mut() {
*element = Some(item);
}

That should still optimize appropriately.

Comment on lines +137 to +139
pub fn pop(&mut self) -> Option<T>
where
Option<T>: Copy,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Some of the most critical API being bounded on Option<T>: Copy, which requires T: Copy, is not really acceptable. It should work without it. This should have tests that confirm it works even if the queue is of Mutex<Vec<T>> or something else painful.

Suggested change
pub fn pop(&mut self) -> Option<T>
where
Option<T>: Copy,
pub fn pop(&mut self) -> Option<T> {

Comment on lines +474 to +475
/// New collection
(unstable, fixed_queue, "1.77.1", Some(126204)),
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We don't use this for library features.

Suggested change
/// New collection
(unstable, fixed_queue, "1.77.1", Some(126204)),

}

#[unstable(feature = "fixed_queue", issue = "126204")]
impl<T: Copy, const N: usize> From<&[T]> for FixedQueue<T, N> {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
impl<T: Copy, const N: usize> From<&[T]> for FixedQueue<T, N> {
impl<T, const N: usize> From<&[T]> for FixedQueue<T, N> {

///
/// [`push`]: FixedQueue::push
/// [`pop`]: FixedQueue::pop
#[derive(Debug)]
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should probably not derive Debug, but rather have an implementation that guarantees that the elements are printed from head to tail.

Comment on lines +258 to +259
// create temporary array to store the results
let mut temp = Vec::with_capacity(end - start);
Copy link
Member

@workingjubilee workingjubilee Jun 10, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

...No.

  • This isn't an array, this is a Vec.
  • This is not an acceptable implementation.

Comment on lines +268 to +272
// Return a slice from the temporary array
// SAFETY: This is safe because temp will live long enough within this function call.
let result = unsafe { slice::from_raw_parts(temp.as_ptr(), temp.len()) };
mem::forget(temp);
result
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This creates a memory leak for performing simple indexing. This should successfully run to completion if you use fixed_queue[0..N] on a FixedQueue of 65535 elements a total of 2147483647 times in a program, without increasing memory usage.

#[derive(Debug)]
#[unstable(feature = "fixed_queue", issue = "126204")]
pub struct FixedQueue<T, const N: usize> {
buffer: [Option<T>; N],
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This representation is... bad. It requires the memory leak in Index<Range<usize>> to yield [T]. Please come back with a version that uses MaybeUninit instead:

Suggested change
buffer: [Option<T>; N],
buffer: [MaybeUninit<T>; N],

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This would, of course, require much more unsafe code in order to implement correctly, with which an overconfident yes-man cannot help. I strongly advise not even trying to ask ChatGPT, Codex, Copilot, or any other code-generation tool that you may or may not be using about unsafe code. They will give you wrong answers, or bad ones.

@workingjubilee
Copy link
Member

workingjubilee commented Jun 10, 2024

I strongly recommend you actually learn to run the standard library build on a local machine... or even a remote one you have full shell access to... if you want to contribute significant data structures to rust-lang/rust, instead of using the GitHub web interface for all of your edits. Apologies if you already are or are having difficulties. I can help on the Rust Zulip if you wish to try.

@workingjubilee
Copy link
Member

workingjubilee commented Jun 10, 2024

I recognize that Rust already has VecDeque, but it's growable and has a more general implementation. Please correct me if I'm wrong, but I believe this might offer a more efficient solution for scenarios where a fixed-size queue is sufficient.

"A more efficient solution for scenarios where..."

This particular clause is borderline irrelevant for deciding whether something is added to the standard library. There are, when you consider significant implementation differences, millions of data structures that are more efficient or optimal in some specific case. The standard library is not necessarily aiming to have millions of data structures. The path to adding API is expanded on in greater length here: https://std-dev-guide.rust-lang.org/development/feature-lifecycle.html

There is probably some appetite for accepting an ArrayVec implementation into the standard library, but I don't see why we would accept a FixedQueue with ArrayVec-like implementation first.

@scottmcm
Copy link
Member

A new data structure

A whole new data structure is a big enough thing that you absolutely need at least an API change proposal for it. It might even end up needing an RFC. As such, I'm actually just going to close this -- a new data structure is a much bigger topic than just a new inherent method here or there.

In particular, there's people working on things like rust-lang/wg-allocators#93 with the goal that VecDeque can be the fixed-size double-ended queue just by changing the allocator, rather than adding another type.

I would suggest that you turn your code into a crate instead of submitting it here. Then people can use it immediately, whereas if you submit it to core people can't use it for a minimum of 11 weeks -- and more likely people won't be able to use it for a year or more. When something doesn't need privileged access to internal stuff, a crate is more useful.

@scottmcm scottmcm closed this Jun 14, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. T-libs-api Relevant to the library API team, which will review and decide on the PR/issue.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants