Skip to content

fix: clarify that fs::rename on unix accepts targets that don't exist#149267

Merged
rust-bors[bot] merged 1 commit into
rust-lang:mainfrom
sftse:c-dev
Jun 20, 2026
Merged

fix: clarify that fs::rename on unix accepts targets that don't exist#149267
rust-bors[bot] merged 1 commit into
rust-lang:mainfrom
sftse:c-dev

Conversation

@sftse

@sftse sftse commented Nov 24, 2025

Copy link
Copy Markdown
Contributor

@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 Nov 24, 2025
@rustbot

rustbot commented Nov 24, 2025

Copy link
Copy Markdown
Collaborator

r? @tgross35

rustbot has assigned @tgross35.
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

@hkBst

hkBst commented Nov 28, 2025

Copy link
Copy Markdown
Member

@sftse do you have link to a good source that documents this?

Comment thread library/std/src/fs.rs Outdated
Comment thread library/std/src/fs.rs Outdated
@sftse

sftse commented Nov 28, 2025

Copy link
Copy Markdown
Contributor Author

This is the current behavior on Linux

    fn dir_to_nonexist_dir() {
        let dir = Path::new("__testdir");
        std::fs::create_dir(dir).unwrap();
        std::fs::create_dir(dir.join("foo")).unwrap();
        std::fs::rename(dir.join("foo"), dir.join("bar")).unwrap();
        assert!(dir.join("bar").is_dir());

        std::fs::remove_dir(dir.join("bar")).unwrap();
        std::fs::remove_dir(dir).unwrap();
    }

rename

int rename(const char *oldpath, const char *newpath);
...
oldpath can specify a directory. In this case, newpath must
either not exist, or it must specify an empty directory.

Comment thread library/std/src/fs.rs Outdated
Comment on lines 2665 to 2673
@@ -2666,8 +2666,8 @@ pub fn symlink_metadata<P: AsRef<Path>>(path: P) -> io::Result<Metadata> {
/// and the `MoveFileExW` or `SetFileInformationByHandle` function on Windows.
///
/// Because of this, the behavior when both `from` and `to` exist differs. On
/// Unix, if `from` is a directory, `to` must also be an (empty) directory. If
/// `from` is not a directory, `to` must also be not a directory. The behavior
/// Unix, if `from` is a directory, `to` must be an empty directory or non-existent. If
/// `from` is not a directory, `to` must also not be a directory. The behavior
/// on Windows is the same on Windows 10 1607 and higher if `FileRenameInfoEx`
/// is supported by the filesystem; otherwise, `from` can be anything, but
/// `to` must *not* be a directory.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

The top of the paragraph already has the qualifier "when both from and to exist", so I think this is slightly redundant as written. This is a bit dense anyway so it wouldn't be bad to clean up the whole section. How about switching to a list format, something like:

This function currently corresponds to the `rename` function on Unix, and
`MoveFileExW` with a fallback to `SetFileInformationByHandle` on Windows.
The exact behavior differs:

- On Unix, when `from` is a directory and `to` exists, `to` must be an empty directory.
- On Unix, when `from` is not a directory and `to` exists, `to` may not be a directory.
- On Windows 10 version 1607 and above, the behavior is the same as Unix if the
  filesystem supports  `FileRenameInfoEx`.
- Otherwise on Windows, `from` can be anything but `to` must not be a directory.
- If `to` does not exist, `from` can be anything.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Sounds good, although grouping the list by first behavior that is common to (most) platforms and then mentioning the caveats and edge cases might be better?

I can't currently tell what all the behaviors are, so I'll either test them or have to take your word for it.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I kind of mirrored the ordering that exists, which I think makes enough sense: you read from top to bottom and stop when you have a match. The final item is a fallback.

I can't currently tell what all the behaviors are, so I'll either test them or have to take your word for it.

I don't think I added anything that wasn't there. I did double check the Windows case, and it should be accurate given we use FILE_RENAME_POSIX_SEMANTICS. The posix/unix behavior is specified at https://pubs.opengroup.org/onlinepubs/9799919799/functions/rename.html and says:

If the old argument names a file that is not a directory and the new argument names a directory, or old names a directory and new names a file that is not a directory, or new names a directory that is not empty, rename() shall fail. Otherwise, if the directory entry named by new exists, it shall be removed and old renamed to new.

So I think what we have matches.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Ah, you were probably talking about the "with a fallback" part. For context there see #131072 and #137528.

It might also be worth turning `rename` into a link to that posix page so anyone who needs more details can find it quick.

@tgross35

tgross35 commented Jan 3, 2026

Copy link
Copy Markdown
Contributor

This needs followup to the above,
@rustbot author

@rustbot rustbot added S-waiting-on-author Status: This is awaiting some action (such as code changes or more information) from the author. and removed S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. labels Jan 3, 2026
@rustbot

rustbot commented Jan 3, 2026

Copy link
Copy Markdown
Collaborator

Reminder, once the PR becomes ready for a review, use @rustbot ready.

@tgross35

Copy link
Copy Markdown
Contributor

@sftse gentle ping, would you be able to do the update from #149267 (comment)?

@sftse

sftse commented Feb 1, 2026

Copy link
Copy Markdown
Contributor Author

Yes, thanks for the ping, I'll get to it.

@Dylan-DPC

Copy link
Copy Markdown
Member

@sftse any updates on this? thanks

@sftse

sftse commented Jun 18, 2026

Copy link
Copy Markdown
Contributor Author

@Dylan-DPC sorry for this. If I don't get to it tomorrow feel free to close, supersede or reassign.

@rustbot

rustbot commented Jun 19, 2026

Copy link
Copy Markdown
Collaborator

This PR was rebased onto a different main commit. Here's a range-diff highlighting what actually changed.

Rebasing is a normal part of keeping PRs up to date, so no action is needed—this note is just to help reviewers.

@sftse sftse force-pushed the c-dev branch 2 times, most recently from cb3f647 to ab1ccf8 Compare June 19, 2026 19:30
Comment thread library/std/src/fs.rs
Comment thread library/std/src/fs.rs

@tgross35 tgross35 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Two small nits then r=me

View changes since this review

@sftse

sftse commented Jun 19, 2026

Copy link
Copy Markdown
Contributor Author

I was not able to figure out whether If to does not exist, from can be anything. really applies to all platforms as the wording seems to imply.

Is this the case in Windows pre 1607?

@tgross35 tgross35 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

LGTM, thank you!

@bors r+ rollup

View changes since this review

@rust-bors

rust-bors Bot commented Jun 19, 2026

Copy link
Copy Markdown
Contributor

📌 Commit d04b878 has been approved by tgross35

It is now in the queue for this repository.

@rust-bors rust-bors Bot 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-author Status: This is awaiting some action (such as code changes or more information) from the author. labels Jun 19, 2026
@tgross35

Copy link
Copy Markdown
Contributor

I was not able to figure out whether If to does not exist, from can be anything. really applies to all platforms as the wording seems to imply.

Is this the case in Windows pre 1607?

I can't think of any reason it wouldn't apply; it should be able to move either files or directories, and if the destination doesn't exist then it doesn't have any reason to care what the source is.

JonathanBrouwer added a commit to JonathanBrouwer/rust that referenced this pull request Jun 19, 2026
fix: clarify that fs::rename on unix accepts targets that don't exist
rust-bors Bot pushed a commit that referenced this pull request Jun 19, 2026
Rollup of 3 pull requests

Successful merges:

 - #149267 (fix: clarify that fs::rename on unix accepts targets that don't exist)
 - #158106 (Expand diagnostic for attributes on macro calls)
 - #158125 (add a test for the `getrandom` fallback)
@rust-bors rust-bors Bot merged commit 044ca3b into rust-lang:main Jun 20, 2026
13 checks passed
@rustbot rustbot added this to the 1.98.0 milestone Jun 20, 2026
rust-timer added a commit that referenced this pull request Jun 20, 2026
Rollup merge of #149267 - sftse:c-dev, r=tgross35

fix: clarify that fs::rename on unix accepts targets that don't exist
@sftse

sftse commented Jun 21, 2026

Copy link
Copy Markdown
Contributor Author

Ok, thanks for reviewing! Took me a while.

@sftse sftse deleted the c-dev branch June 21, 2026 14:07
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 Relevant to the library team, which will review and decide on the PR/issue.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants