Skip to content
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

std: edit [T]::swap docs #113064

Merged
merged 1 commit into from
Jul 8, 2023
Merged

Conversation

marcospb19
Copy link
Contributor

@marcospb19 marcospb19 commented Jun 26, 2023

Add a note about what happens when index arguments are equal.

@rustbot
Copy link
Collaborator

rustbot commented Jun 26, 2023

r? @Mark-Simulacrum

(rustbot has picked a reviewer for you, use r? to override)

@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 26, 2023
@clubby789
Copy link
Contributor

Is this true? Reading the code (and on godbolt) there does not appear to be any special-case for equality.

@marcospb19
Copy link
Contributor Author

marcospb19 commented Jun 26, 2023

Is this true?

From local testing it looks like it is, otherwise, I can add a check.

pub const fn swap(&mut self, a: usize, b: usize) {
    if a == b {
        return;
    }
    ...

Well... We'd need to think if somebody might be relying on it's weird behavior, in case it has one.

(and on godbolt)

There's optimization going on (I called v.swap(0, 0) and it became no-op assembly, just ret), I believe, you need to pass a and b to std::hint::black_box before feeding them into Vec::swap, if you're willing to analyze the compiler output.

@marcospb19
Copy link
Contributor Author

marcospb19 commented Jun 26, 2023

Confirming, it is true by the underlying implementation of ptr::swap.

pub const unsafe fn swap<T>(x: *mut T, y: *mut T) {
    // Give ourselves some scratch space to work with.
    // We do not have to worry about drops: `MaybeUninit` does nothing when dropped.
    let mut tmp = MaybeUninit::<T>::uninit();

    // Perform the swap
    // SAFETY: the caller must guarantee that `x` and `y` are
    // valid for writes and properly aligned. `tmp` cannot be
    // overlapping either `x` or `y` because `tmp` was just allocated
    // on the stack as a separate allocated object.
    unsafe {
        copy_nonoverlapping(x, tmp.as_mut_ptr(), 1);
        copy(y, x, 1); // `x` and `y` may overlap
        copy_nonoverlapping(tmp.as_ptr(), y, 1);
    }
}

@clubby789
Copy link
Contributor

clubby789 commented Jun 26, 2023

Ah, I misunderstood (I thought you meant the values at a and b, rather than the indexes themselves). Still, ptr::swap does not seem to check if the indexes/pointers are the same - indeed, the call to copy explicitly mentions that they may overlap so copy_nonoverlapping may not be used.
Being a no-op is not guarenteed as far as I can tell, although it happens to be optimised out when the optimiser can see the two indees are the same i.e. this example will panic

@marcospb19
Copy link
Contributor Author

marcospb19 commented Jun 26, 2023

@clubby789 I believe the snippet I sent you above shows that it is guaranteed, specially this part:

copy_nonoverlapping(x, tmp.as_mut_ptr(), 1);
copy(y, x, 1); // `x` and `y` may overlap
copy_nonoverlapping(tmp.as_ptr(), y, 1);

You can interpret them as

*tmp = *x;   // No overlap
*x = *y;    // Might overlap
*y = *tmp; // No overlap

Now assume x and y point to the same location x:

*tmp = *x; // (1)
*x = *x;   // (2)
*x = *tmp; // (3)

Assignment in 2 is never read, because it was overwritten by value in assignment 3, so you effectively have:

*tmp = *x; // (1)
*x = *tmp; // (3)

Which is guaranteed to work because tmp is a temporary stack-allocated buffer (impossible to overlap with the rest).

@clubby789
Copy link
Contributor

clubby789 commented Jun 26, 2023

This is not a no-op though - it's 3 moves (which can panic if OOB).

@marcospb19
Copy link
Contributor Author

marcospb19 commented Jun 26, 2023

Oh, I'm sorry! By no-op I meant "it doesn't change anything, there is no mutation or swaps".

Do you suggest I change the comment like so?

-/// If `a` equals to `b`, this is a no-op.
+/// If `a` equals to `b`, no swaps are done.

@Noratrieb Noratrieb 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 27, 2023
@Mark-Simulacrum
Copy link
Member

I don't think the implementation guarantees that no swaps are done. In debug mode we swap even with equal arguments: https://rust.godbolt.org/z/z174eW9eh

I think in most cases this is fine, I wouldn't add a check to the swap code to skip the check as it's probably true that the vast majority of swaps have non-equal arguments and the extra check is a wasted computation.

Add a note telling that no elements change when arguments are equal
@marcospb19 marcospb19 changed the title std: edit Vec::swap docs std: edit [T]::swap docs Jun 27, 2023
@marcospb19 marcospb19 force-pushed the add-note-in-vec-swap-docs branch from 5ebf984 to 30c61ee Compare June 27, 2023 21:12
Comment on lines +854 to +855
/// If `a` equals to `b`, it's guaranteed that elements won't change value.
///
Copy link
Contributor Author

Choose a reason for hiding this comment

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

I edited the text accordingly, now it should make more sense?

@Mark-Simulacrum
Copy link
Member

@bors r+ rollup

I guess this seems OK, though I'm not sure that it's very meaningful to add this to the docs.

@bors
Copy link
Contributor

bors commented Jul 8, 2023

📌 Commit 30c61ee has been approved by Mark-Simulacrum

It is now in the queue for this repository.

@bors bors added S-waiting-on-bors Status: Waiting on bors to run and complete tests. Bors will change the label on completion. and removed S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. labels Jul 8, 2023
matthiaskrgr added a commit to matthiaskrgr/rust that referenced this pull request Jul 8, 2023
…cs, r=Mark-Simulacrum

std: edit [T]::swap docs

Add a note about what happens when index arguments are equal.
bors added a commit to rust-lang-ci/rust that referenced this pull request Jul 8, 2023
…iaskrgr

Rollup of 6 pull requests

Successful merges:

 - rust-lang#113005 (Don't call `query_normalize` when reporting similar impls)
 - rust-lang#113064 (std: edit [T]::swap docs)
 - rust-lang#113138 (Add release notes for 1.71.0)
 - rust-lang#113217 (resolve typerelative ctors to adt)
 - rust-lang#113254 (Use consistent formatting in Readme)
 - rust-lang#113482 (Migrate GUI colors test to original CSS color format)

r? `@ghost`
`@rustbot` modify labels: rollup
@bors bors merged commit 8c56299 into rust-lang:master Jul 8, 2023
@rustbot rustbot added this to the 1.72.0 milestone Jul 8, 2023
@marcospb19 marcospb19 deleted the add-note-in-vec-swap-docs branch July 11, 2023 01:18
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
S-waiting-on-bors Status: Waiting on bors to run and complete tests. Bors will change the label on completion. 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