Skip to content

Commit 0ca4ea0

Browse files
committed
Add PathLike trait
1 parent 5605ed8 commit 0ca4ea0

File tree

5 files changed

+84
-57
lines changed

5 files changed

+84
-57
lines changed

library/std/src/env.rs

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,9 @@ use crate::error::Error;
1717
use crate::ffi::{OsStr, OsString};
1818
use crate::fmt;
1919
use crate::io;
20-
use crate::path::{Path, PathBuf};
20+
#[cfg(doc)]
21+
use crate::path::Path;
22+
use crate::path::{PathBuf, PathLike};
2123
use crate::sys;
2224
use crate::sys::os as os_imp;
2325

@@ -80,8 +82,8 @@ pub fn current_dir() -> io::Result<PathBuf> {
8082
/// ```
8183
#[doc(alias = "chdir")]
8284
#[stable(feature = "env", since = "1.0.0")]
83-
pub fn set_current_dir<P: AsRef<Path>>(path: P) -> io::Result<()> {
84-
os_imp::chdir(path.as_ref())
85+
pub fn set_current_dir<P: PathLike>(path: P) -> io::Result<()> {
86+
path.with_path(os_imp::chdir)
8587
}
8688

8789
/// An iterator over a snapshot of the environment variables of this process.

library/std/src/fs.rs

Lines changed: 49 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ mod tests;
1414
use crate::ffi::OsString;
1515
use crate::fmt;
1616
use crate::io::{self, BorrowedCursor, IoSlice, IoSliceMut, Read, Seek, SeekFrom, Write};
17-
use crate::path::{Path, PathBuf};
17+
use crate::path::{Path, PathBuf, PathLike};
1818
use crate::sys::fs as fs_imp;
1919
use crate::sys_common::{AsInner, AsInnerMut, FromInner, IntoInner};
2020
use crate::time::SystemTime;
@@ -246,14 +246,14 @@ pub struct DirBuilder {
246246
/// }
247247
/// ```
248248
#[stable(feature = "fs_read_write_bytes", since = "1.26.0")]
249-
pub fn read<P: AsRef<Path>>(path: P) -> io::Result<Vec<u8>> {
249+
pub fn read<P: PathLike>(path: P) -> io::Result<Vec<u8>> {
250250
fn inner(path: &Path) -> io::Result<Vec<u8>> {
251251
let mut file = File::open(path)?;
252252
let mut bytes = Vec::new();
253253
file.read_to_end(&mut bytes)?;
254254
Ok(bytes)
255255
}
256-
inner(path.as_ref())
256+
path.with_path(inner)
257257
}
258258

259259
/// Read the entire contents of a file into a string.
@@ -285,14 +285,14 @@ pub fn read<P: AsRef<Path>>(path: P) -> io::Result<Vec<u8>> {
285285
/// }
286286
/// ```
287287
#[stable(feature = "fs_read_write", since = "1.26.0")]
288-
pub fn read_to_string<P: AsRef<Path>>(path: P) -> io::Result<String> {
288+
pub fn read_to_string<P: PathLike>(path: P) -> io::Result<String> {
289289
fn inner(path: &Path) -> io::Result<String> {
290290
let mut file = File::open(path)?;
291291
let mut string = String::new();
292292
file.read_to_string(&mut string)?;
293293
Ok(string)
294294
}
295-
inner(path.as_ref())
295+
path.with_path(inner)
296296
}
297297

298298
/// Write a slice as the entire contents of a file.
@@ -320,11 +320,11 @@ pub fn read_to_string<P: AsRef<Path>>(path: P) -> io::Result<String> {
320320
/// }
321321
/// ```
322322
#[stable(feature = "fs_read_write_bytes", since = "1.26.0")]
323-
pub fn write<P: AsRef<Path>, C: AsRef<[u8]>>(path: P, contents: C) -> io::Result<()> {
323+
pub fn write<P: PathLike, C: AsRef<[u8]>>(path: P, contents: C) -> io::Result<()> {
324324
fn inner(path: &Path, contents: &[u8]) -> io::Result<()> {
325325
File::create(path)?.write_all(contents)
326326
}
327-
inner(path.as_ref(), contents.as_ref())
327+
path.with_path(|path| inner(path.as_ref(), contents.as_ref()))
328328
}
329329

330330
impl File {
@@ -348,8 +348,8 @@ impl File {
348348
/// }
349349
/// ```
350350
#[stable(feature = "rust1", since = "1.0.0")]
351-
pub fn open<P: AsRef<Path>>(path: P) -> io::Result<File> {
352-
OpenOptions::new().read(true).open(path.as_ref())
351+
pub fn open<P: PathLike>(path: P) -> io::Result<File> {
352+
OpenOptions::new().read(true).open(path)
353353
}
354354

355355
/// Opens a file in write-only mode.
@@ -373,8 +373,8 @@ impl File {
373373
/// }
374374
/// ```
375375
#[stable(feature = "rust1", since = "1.0.0")]
376-
pub fn create<P: AsRef<Path>>(path: P) -> io::Result<File> {
377-
OpenOptions::new().write(true).create(true).truncate(true).open(path.as_ref())
376+
pub fn create<P: PathLike>(path: P) -> io::Result<File> {
377+
OpenOptions::new().write(true).create(true).truncate(true).open(path)
378378
}
379379

380380
/// Creates a new file in read-write mode; error if the file exists.
@@ -402,8 +402,8 @@ impl File {
402402
/// }
403403
/// ```
404404
#[unstable(feature = "file_create_new", issue = "none")]
405-
pub fn create_new<P: AsRef<Path>>(path: P) -> io::Result<File> {
406-
OpenOptions::new().read(true).write(true).create_new(true).open(path.as_ref())
405+
pub fn create_new<P: PathLike>(path: P) -> io::Result<File> {
406+
OpenOptions::new().read(true).write(true).create_new(true).open(path)
407407
}
408408

409409
/// Returns a new OpenOptions object.
@@ -1052,8 +1052,8 @@ impl OpenOptions {
10521052
/// [`NotFound`]: io::ErrorKind::NotFound
10531053
/// [`PermissionDenied`]: io::ErrorKind::PermissionDenied
10541054
#[stable(feature = "rust1", since = "1.0.0")]
1055-
pub fn open<P: AsRef<Path>>(&self, path: P) -> io::Result<File> {
1056-
self._open(path.as_ref())
1055+
pub fn open<P: PathLike>(&self, path: P) -> io::Result<File> {
1056+
path.with_path(|path| self._open(path))
10571057
}
10581058

10591059
fn _open(&self, path: &Path) -> io::Result<File> {
@@ -1723,8 +1723,8 @@ impl AsInner<fs_imp::DirEntry> for DirEntry {
17231723
/// }
17241724
/// ```
17251725
#[stable(feature = "rust1", since = "1.0.0")]
1726-
pub fn remove_file<P: AsRef<Path>>(path: P) -> io::Result<()> {
1727-
fs_imp::unlink(path.as_ref())
1726+
pub fn remove_file<P: PathLike>(path: P) -> io::Result<()> {
1727+
path.with_path(fs_imp::unlink)
17281728
}
17291729

17301730
/// Given a path, query the file system to get information about a file,
@@ -1761,8 +1761,8 @@ pub fn remove_file<P: AsRef<Path>>(path: P) -> io::Result<()> {
17611761
/// }
17621762
/// ```
17631763
#[stable(feature = "rust1", since = "1.0.0")]
1764-
pub fn metadata<P: AsRef<Path>>(path: P) -> io::Result<Metadata> {
1765-
fs_imp::stat(path.as_ref()).map(Metadata)
1764+
pub fn metadata<P: PathLike>(path: P) -> io::Result<Metadata> {
1765+
path.with_path(fs_imp::stat).map(Metadata)
17661766
}
17671767

17681768
/// Query the metadata about a file without following symlinks.
@@ -1795,8 +1795,8 @@ pub fn metadata<P: AsRef<Path>>(path: P) -> io::Result<Metadata> {
17951795
/// }
17961796
/// ```
17971797
#[stable(feature = "symlink_metadata", since = "1.1.0")]
1798-
pub fn symlink_metadata<P: AsRef<Path>>(path: P) -> io::Result<Metadata> {
1799-
fs_imp::lstat(path.as_ref()).map(Metadata)
1798+
pub fn symlink_metadata<P: PathLike>(path: P) -> io::Result<Metadata> {
1799+
path.with_path(fs_imp::lstat).map(Metadata)
18001800
}
18011801

18021802
/// Rename a file or directory to a new name, replacing the original file if
@@ -1838,8 +1838,8 @@ pub fn symlink_metadata<P: AsRef<Path>>(path: P) -> io::Result<Metadata> {
18381838
/// }
18391839
/// ```
18401840
#[stable(feature = "rust1", since = "1.0.0")]
1841-
pub fn rename<P: AsRef<Path>, Q: AsRef<Path>>(from: P, to: Q) -> io::Result<()> {
1842-
fs_imp::rename(from.as_ref(), to.as_ref())
1841+
pub fn rename<P: PathLike, Q: PathLike>(from: P, to: Q) -> io::Result<()> {
1842+
from.with_path(|from| to.with_path(|to| fs_imp::rename(from, to)))
18431843
}
18441844

18451845
/// Copies the contents of one file to another. This function will also
@@ -1896,8 +1896,8 @@ pub fn rename<P: AsRef<Path>, Q: AsRef<Path>>(from: P, to: Q) -> io::Result<()>
18961896
/// }
18971897
/// ```
18981898
#[stable(feature = "rust1", since = "1.0.0")]
1899-
pub fn copy<P: AsRef<Path>, Q: AsRef<Path>>(from: P, to: Q) -> io::Result<u64> {
1900-
fs_imp::copy(from.as_ref(), to.as_ref())
1899+
pub fn copy<P: PathLike, Q: PathLike>(from: P, to: Q) -> io::Result<u64> {
1900+
from.with_path(|from| to.with_path(|to| fs_imp::copy(from, to)))
19011901
}
19021902

19031903
/// Creates a new hard link on the filesystem.
@@ -1940,8 +1940,8 @@ pub fn copy<P: AsRef<Path>, Q: AsRef<Path>>(from: P, to: Q) -> io::Result<u64> {
19401940
/// }
19411941
/// ```
19421942
#[stable(feature = "rust1", since = "1.0.0")]
1943-
pub fn hard_link<P: AsRef<Path>, Q: AsRef<Path>>(original: P, link: Q) -> io::Result<()> {
1944-
fs_imp::link(original.as_ref(), link.as_ref())
1943+
pub fn hard_link<P: PathLike, Q: PathLike>(original: P, link: Q) -> io::Result<()> {
1944+
original.with_path(|original| link.with_path(|link| fs_imp::link(original, link)))
19451945
}
19461946

19471947
/// Creates a new symbolic link on the filesystem.
@@ -1972,8 +1972,8 @@ pub fn hard_link<P: AsRef<Path>, Q: AsRef<Path>>(original: P, link: Q) -> io::Re
19721972
note = "replaced with std::os::unix::fs::symlink and \
19731973
std::os::windows::fs::{symlink_file, symlink_dir}"
19741974
)]
1975-
pub fn soft_link<P: AsRef<Path>, Q: AsRef<Path>>(original: P, link: Q) -> io::Result<()> {
1976-
fs_imp::symlink(original.as_ref(), link.as_ref())
1975+
pub fn soft_link<P: PathLike, Q: PathLike>(original: P, link: Q) -> io::Result<()> {
1976+
original.with_path(|original| link.with_path(|link| fs_imp::symlink(original, link)))
19771977
}
19781978

19791979
/// Reads a symbolic link, returning the file that the link points to.
@@ -2006,8 +2006,8 @@ pub fn soft_link<P: AsRef<Path>, Q: AsRef<Path>>(original: P, link: Q) -> io::Re
20062006
/// }
20072007
/// ```
20082008
#[stable(feature = "rust1", since = "1.0.0")]
2009-
pub fn read_link<P: AsRef<Path>>(path: P) -> io::Result<PathBuf> {
2010-
fs_imp::readlink(path.as_ref())
2009+
pub fn read_link<P: PathLike>(path: P) -> io::Result<PathBuf> {
2010+
path.with_path(fs_imp::readlink)
20112011
}
20122012

20132013
/// Returns the canonical, absolute form of a path with all intermediate
@@ -2049,8 +2049,8 @@ pub fn read_link<P: AsRef<Path>>(path: P) -> io::Result<PathBuf> {
20492049
#[doc(alias = "realpath")]
20502050
#[doc(alias = "GetFinalPathNameByHandle")]
20512051
#[stable(feature = "fs_canonicalize", since = "1.5.0")]
2052-
pub fn canonicalize<P: AsRef<Path>>(path: P) -> io::Result<PathBuf> {
2053-
fs_imp::canonicalize(path.as_ref())
2052+
pub fn canonicalize<P: PathLike>(path: P) -> io::Result<PathBuf> {
2053+
path.with_path(fs_imp::canonicalize)
20542054
}
20552055

20562056
/// Creates a new, empty directory at the provided path
@@ -2090,8 +2090,8 @@ pub fn canonicalize<P: AsRef<Path>>(path: P) -> io::Result<PathBuf> {
20902090
/// ```
20912091
#[doc(alias = "mkdir")]
20922092
#[stable(feature = "rust1", since = "1.0.0")]
2093-
pub fn create_dir<P: AsRef<Path>>(path: P) -> io::Result<()> {
2094-
DirBuilder::new().create(path.as_ref())
2093+
pub fn create_dir<P: PathLike>(path: P) -> io::Result<()> {
2094+
DirBuilder::new().create(path)
20952095
}
20962096

20972097
/// Recursively create a directory and all of its parent components if they
@@ -2134,8 +2134,8 @@ pub fn create_dir<P: AsRef<Path>>(path: P) -> io::Result<()> {
21342134
/// }
21352135
/// ```
21362136
#[stable(feature = "rust1", since = "1.0.0")]
2137-
pub fn create_dir_all<P: AsRef<Path>>(path: P) -> io::Result<()> {
2138-
DirBuilder::new().recursive(true).create(path.as_ref())
2137+
pub fn create_dir_all<P: PathLike>(path: P) -> io::Result<()> {
2138+
DirBuilder::new().recursive(true).create(path)
21392139
}
21402140

21412141
/// Removes an empty directory.
@@ -2170,8 +2170,8 @@ pub fn create_dir_all<P: AsRef<Path>>(path: P) -> io::Result<()> {
21702170
/// ```
21712171
#[doc(alias = "rmdir")]
21722172
#[stable(feature = "rust1", since = "1.0.0")]
2173-
pub fn remove_dir<P: AsRef<Path>>(path: P) -> io::Result<()> {
2174-
fs_imp::rmdir(path.as_ref())
2173+
pub fn remove_dir<P: PathLike>(path: P) -> io::Result<()> {
2174+
path.with_path(fs_imp::rmdir)
21752175
}
21762176

21772177
/// Removes a directory at this path, after removing all its contents. Use
@@ -2212,8 +2212,8 @@ pub fn remove_dir<P: AsRef<Path>>(path: P) -> io::Result<()> {
22122212
/// }
22132213
/// ```
22142214
#[stable(feature = "rust1", since = "1.0.0")]
2215-
pub fn remove_dir_all<P: AsRef<Path>>(path: P) -> io::Result<()> {
2216-
fs_imp::remove_dir_all(path.as_ref())
2215+
pub fn remove_dir_all<P: PathLike>(path: P) -> io::Result<()> {
2216+
path.with_path(fs_imp::remove_dir_all)
22172217
}
22182218

22192219
/// Returns an iterator over the entries within a directory.
@@ -2287,8 +2287,8 @@ pub fn remove_dir_all<P: AsRef<Path>>(path: P) -> io::Result<()> {
22872287
/// }
22882288
/// ```
22892289
#[stable(feature = "rust1", since = "1.0.0")]
2290-
pub fn read_dir<P: AsRef<Path>>(path: P) -> io::Result<ReadDir> {
2291-
fs_imp::readdir(path.as_ref()).map(ReadDir)
2290+
pub fn read_dir<P: PathLike>(path: P) -> io::Result<ReadDir> {
2291+
path.with_path(fs_imp::readdir).map(ReadDir)
22922292
}
22932293

22942294
/// Changes the permissions found on a file or a directory.
@@ -2322,8 +2322,8 @@ pub fn read_dir<P: AsRef<Path>>(path: P) -> io::Result<ReadDir> {
23222322
/// }
23232323
/// ```
23242324
#[stable(feature = "set_permissions", since = "1.1.0")]
2325-
pub fn set_permissions<P: AsRef<Path>>(path: P, perm: Permissions) -> io::Result<()> {
2326-
fs_imp::set_perm(path.as_ref(), perm.0)
2325+
pub fn set_permissions<P: PathLike>(path: P, perm: Permissions) -> io::Result<()> {
2326+
path.with_path(|path| fs_imp::set_perm(path, perm.0))
23272327
}
23282328

23292329
impl DirBuilder {
@@ -2382,8 +2382,8 @@ impl DirBuilder {
23822382
/// assert!(fs::metadata(path).unwrap().is_dir());
23832383
/// ```
23842384
#[stable(feature = "dir_builder", since = "1.6.0")]
2385-
pub fn create<P: AsRef<Path>>(&self, path: P) -> io::Result<()> {
2386-
self._create(path.as_ref())
2385+
pub fn create<P: PathLike>(&self, path: P) -> io::Result<()> {
2386+
path.with_path(|path| self._create(path))
23872387
}
23882388

23892389
fn _create(&self, path: &Path) -> io::Result<()> {
@@ -2452,6 +2452,6 @@ impl AsInnerMut<fs_imp::DirBuilder> for DirBuilder {
24522452
// instead.
24532453
#[unstable(feature = "fs_try_exists", issue = "83186")]
24542454
#[inline]
2455-
pub fn try_exists<P: AsRef<Path>>(path: P) -> io::Result<bool> {
2456-
fs_imp::try_exists(path.as_ref())
2455+
pub fn try_exists<P: PathLike>(path: P) -> io::Result<bool> {
2456+
path.with_path(fs_imp::try_exists)
24572457
}

library/std/src/path.rs

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1109,6 +1109,30 @@ impl FusedIterator for Ancestors<'_> {}
11091109
// Basic types and traits
11101110
////////////////////////////////////////////////////////////////////////////////
11111111

1112+
/// # Stable use
1113+
///
1114+
/// Any function that requires a path may take a `PathLike` type.
1115+
///
1116+
/// These types include [`OsStr`], [`Path`] and [`str`] as well as their owned
1117+
/// counterparts [`OsString`], [`PathBuf`] and [`String`].
1118+
///
1119+
/// # Unstable use
1120+
///
1121+
/// The `PathLike` trait can be implemented for custom path types. This requires
1122+
/// a nightly compiler with the `path_like` feature enabled. Note that this
1123+
/// trait is unstable and highly likely to change between nightly versions.
1124+
#[unstable(feature = "path_like", issue = "none")]
1125+
pub trait PathLike {
1126+
/// Convert to a `Path` reference.
1127+
fn with_path<T, F: FnOnce(&Path) -> T>(&self, f: F) -> T;
1128+
}
1129+
#[unstable(feature = "path_like", issue = "none")]
1130+
impl<P: AsRef<Path>> PathLike for P {
1131+
fn with_path<T, F: FnOnce(&Path) -> T>(&self, f: F) -> T {
1132+
f(self.as_ref())
1133+
}
1134+
}
1135+
11121136
/// An owned, mutable path (akin to [`String`]).
11131137
///
11141138
/// This type provides methods like [`push`] and [`set_extension`] that mutate

library/std/src/process.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -112,7 +112,7 @@ use crate::fmt;
112112
use crate::fs;
113113
use crate::io::{self, IoSlice, IoSliceMut};
114114
use crate::num::NonZeroI32;
115-
use crate::path::Path;
115+
use crate::path::{Path, PathLike};
116116
use crate::str;
117117
use crate::sys::pipe::{read2, AnonPipe};
118118
use crate::sys::process as imp;
@@ -769,8 +769,8 @@ impl Command {
769769
///
770770
/// [`canonicalize`]: crate::fs::canonicalize
771771
#[stable(feature = "process", since = "1.0.0")]
772-
pub fn current_dir<P: AsRef<Path>>(&mut self, dir: P) -> &mut Command {
773-
self.inner.cwd(dir.as_ref().as_ref());
772+
pub fn current_dir<P: PathLike>(&mut self, dir: P) -> &mut Command {
773+
dir.with_path(|dir| self.inner.cwd(dir.as_ref()));
774774
self
775775
}
776776

src/test/ui/async-await/issue-72442.stderr

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,12 @@ LL | let mut f = File::open(path.to_str())?;
66
| |
77
| required by a bound introduced by this call
88
|
9+
= note: required for `Option<&str>` to implement `PathLike`
910
note: required by a bound in `File::open`
1011
--> $SRC_DIR/std/src/fs.rs:LL:COL
1112
|
12-
LL | pub fn open<P: AsRef<Path>>(path: P) -> io::Result<File> {
13-
| ^^^^^^^^^^^ required by this bound in `File::open`
13+
LL | pub fn open<P: PathLike>(path: P) -> io::Result<File> {
14+
| ^^^^^^^^ required by this bound in `File::open`
1415

1516
error: aborting due to previous error
1617

0 commit comments

Comments
 (0)