Skip to content

Commit f9fa13f

Browse files
committed
Auto merge of #85746 - m-ou-se:io-error-other, r=joshtriplett
Redefine `ErrorKind::Other` and stop using it in std. This implements the idea I shared yesterday in the libs meeting when we were discussing how to handle adding new `ErrorKind`s to the standard library: This redefines `Other` to be for *user defined errors only*, and changes all uses of `Other` in the standard library to a `#[doc(hidden)]` and permanently `#[unstable]` `ErrorKind` that users can not match on. This ensures that adding `ErrorKind`s at a later point in time is not a breaking change, since the user couldn't match on these errors anyway. This way, we use the `#[non_exhaustive]` property of the enum in a more effective way. Open questions: - How do we check this change doesn't cause too much breakage? Will a crate run help and be enough? - How do we ensure we don't accidentally start using `Other` again in the standard library? We don't have a `pub(not crate)` or `#[deprecated(in this crate only)]`. cc #79965 cc `@rust-lang/libs` `@ijackson` r? `@dtolnay`
2 parents 1aa6c7c + cc90733 commit f9fa13f

File tree

24 files changed

+99
-74
lines changed

24 files changed

+99
-74
lines changed

library/std/src/fs.rs

+6-6
Original file line numberDiff line numberDiff line change
@@ -880,8 +880,7 @@ impl OpenOptions {
880880
/// This function will return an error under a number of different
881881
/// circumstances. Some of these error conditions are listed here, together
882882
/// with their [`io::ErrorKind`]. The mapping to [`io::ErrorKind`]s is not
883-
/// part of the compatibility contract of the function, especially the
884-
/// [`Other`] kind might change to more specific kinds in the future.
883+
/// part of the compatibility contract of the function.
885884
///
886885
/// * [`NotFound`]: The specified file does not exist and neither `create`
887886
/// or `create_new` is set.
@@ -895,9 +894,11 @@ impl OpenOptions {
895894
/// exists.
896895
/// * [`InvalidInput`]: Invalid combinations of open options (truncate
897896
/// without write access, no access mode set, etc.).
898-
/// * [`Other`]: One of the directory components of the specified file path
897+
///
898+
/// The following errors don't match any existing [`io::ErrorKind`] at the moment:
899+
/// * One of the directory components of the specified file path
899900
/// was not, in fact, a directory.
900-
/// * [`Other`]: Filesystem-level errors: full disk, write permission
901+
/// * Filesystem-level errors: full disk, write permission
901902
/// requested on a read-only file system, exceeded disk quota, too many
902903
/// open files, too long filename, too many symbolic links in the
903904
/// specified path (Unix-like systems only), etc.
@@ -913,7 +914,6 @@ impl OpenOptions {
913914
/// [`AlreadyExists`]: io::ErrorKind::AlreadyExists
914915
/// [`InvalidInput`]: io::ErrorKind::InvalidInput
915916
/// [`NotFound`]: io::ErrorKind::NotFound
916-
/// [`Other`]: io::ErrorKind::Other
917917
/// [`PermissionDenied`]: io::ErrorKind::PermissionDenied
918918
#[stable(feature = "rust1", since = "1.0.0")]
919919
pub fn open<P: AsRef<Path>>(&self, path: P) -> io::Result<File> {
@@ -2216,7 +2216,7 @@ impl DirBuilder {
22162216
Some(p) => self.create_dir_all(p)?,
22172217
None => {
22182218
return Err(io::Error::new_const(
2219-
io::ErrorKind::Other,
2219+
io::ErrorKind::Uncategorized,
22202220
&"failed to create whole tree",
22212221
));
22222222
}

library/std/src/fs/tests.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -1329,7 +1329,8 @@ fn metadata_access_times() {
13291329
match (a.created(), b.created()) {
13301330
(Ok(t1), Ok(t2)) => assert!(t1 <= t2),
13311331
(Err(e1), Err(e2))
1332-
if e1.kind() == ErrorKind::Other && e2.kind() == ErrorKind::Other
1332+
if e1.kind() == ErrorKind::Uncategorized
1333+
&& e2.kind() == ErrorKind::Uncategorized
13331334
|| e1.kind() == ErrorKind::Unsupported
13341335
&& e2.kind() == ErrorKind::Unsupported => {}
13351336
(a, b) => {

library/std/src/io/error.rs

+22-7
Original file line numberDiff line numberDiff line change
@@ -163,12 +163,17 @@ pub enum ErrorKind {
163163
/// Interrupted operations can typically be retried.
164164
#[stable(feature = "rust1", since = "1.0.0")]
165165
Interrupted,
166-
/// Any I/O error not part of this list.
166+
167+
/// A custom error that does not fall under any other I/O error kind.
167168
///
168-
/// Errors that are `Other` now may move to a different or a new
169-
/// [`ErrorKind`] variant in the future. It is not recommended to match
170-
/// an error against `Other` and to expect any additional characteristics,
171-
/// e.g., a specific [`Error::raw_os_error`] return value.
169+
/// This can be used to construct your own [`Error`]s that do not match any
170+
/// [`ErrorKind`].
171+
///
172+
/// This [`ErrorKind`] is not used by the standard library.
173+
///
174+
/// Errors from the standard library that do not fall under any of the I/O
175+
/// error kinds cannot be `match`ed on, and will only match a wildcard (`_`) pattern.
176+
/// New [`ErrorKind`]s might be added in the future for some of those.
172177
#[stable(feature = "rust1", since = "1.0.0")]
173178
Other,
174179

@@ -191,6 +196,15 @@ pub enum ErrorKind {
191196
/// to allocate enough memory.
192197
#[stable(feature = "out_of_memory_error", since = "1.54.0")]
193198
OutOfMemory,
199+
200+
/// Any I/O error from the standard library that's not part of this list.
201+
///
202+
/// Errors that are `Uncategorized` now may move to a different or a new
203+
/// [`ErrorKind`] variant in the future. It is not recommended to match
204+
/// an error against `Uncategorized`; use a wildcard match (`_`) instead.
205+
#[unstable(feature = "io_error_uncategorized", issue = "none")]
206+
#[doc(hidden)]
207+
Uncategorized,
194208
}
195209

196210
impl ErrorKind {
@@ -212,10 +226,11 @@ impl ErrorKind {
212226
ErrorKind::TimedOut => "timed out",
213227
ErrorKind::WriteZero => "write zero",
214228
ErrorKind::Interrupted => "operation interrupted",
215-
ErrorKind::Other => "other os error",
216229
ErrorKind::UnexpectedEof => "unexpected end of file",
217230
ErrorKind::Unsupported => "unsupported",
218231
ErrorKind::OutOfMemory => "out of memory",
232+
ErrorKind::Other => "other error",
233+
ErrorKind::Uncategorized => "uncategorized error",
219234
}
220235
}
221236
}
@@ -538,7 +553,7 @@ impl Error {
538553
/// }
539554
///
540555
/// fn main() {
541-
/// // Will print "Other".
556+
/// // Will print "Uncategorized".
542557
/// print_error(Error::last_os_error());
543558
/// // Will print "AddrInUse".
544559
/// print_error(Error::new(ErrorKind::AddrInUse, "oh no!"));

library/std/src/io/mod.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -1644,7 +1644,7 @@ pub trait Write {
16441644
if output.error.is_err() {
16451645
output.error
16461646
} else {
1647-
Err(Error::new_const(ErrorKind::Other, &"formatter error"))
1647+
Err(Error::new_const(ErrorKind::Uncategorized, &"formatter error"))
16481648
}
16491649
}
16501650
}

library/std/src/net/tcp/tests.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -342,7 +342,7 @@ fn double_bind() {
342342
Err(e) => {
343343
assert!(
344344
e.kind() == ErrorKind::ConnectionRefused
345-
|| e.kind() == ErrorKind::Other
345+
|| e.kind() == ErrorKind::Uncategorized
346346
|| e.kind() == ErrorKind::AddrInUse,
347347
"unknown error: {} {:?}",
348348
e,

library/std/src/os/wasi/fs.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -532,5 +532,6 @@ pub fn symlink_path<P: AsRef<Path>, U: AsRef<Path>>(old_path: P, new_path: U) ->
532532
}
533533

534534
fn osstr2str(f: &OsStr) -> io::Result<&str> {
535-
f.to_str().ok_or_else(|| io::Error::new_const(io::ErrorKind::Other, &"input must be utf-8"))
535+
f.to_str()
536+
.ok_or_else(|| io::Error::new_const(io::ErrorKind::Uncategorized, &"input must be utf-8"))
536537
}

library/std/src/process.rs

+1-3
Original file line numberDiff line numberDiff line change
@@ -1658,8 +1658,7 @@ impl Child {
16581658
/// Forces the child process to exit. If the child has already exited, an [`InvalidInput`]
16591659
/// error is returned.
16601660
///
1661-
/// The mapping to [`ErrorKind`]s is not part of the compatibility contract of the function,
1662-
/// especially the [`Other`] kind might change to more specific kinds in the future.
1661+
/// The mapping to [`ErrorKind`]s is not part of the compatibility contract of the function.
16631662
///
16641663
/// This is equivalent to sending a SIGKILL on Unix platforms.
16651664
///
@@ -1680,7 +1679,6 @@ impl Child {
16801679
///
16811680
/// [`ErrorKind`]: io::ErrorKind
16821681
/// [`InvalidInput`]: io::ErrorKind::InvalidInput
1683-
/// [`Other`]: io::ErrorKind::Other
16841682
#[stable(feature = "process", since = "1.0.0")]
16851683
pub fn kill(&mut self) -> io::Result<()> {
16861684
self.handle.kill()

library/std/src/sys/hermit/mod.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -149,7 +149,7 @@ pub fn decode_error_kind(errno: i32) -> ErrorKind {
149149
x if x == 1 as i32 => ErrorKind::PermissionDenied,
150150
x if x == 32 as i32 => ErrorKind::BrokenPipe,
151151
x if x == 110 as i32 => ErrorKind::TimedOut,
152-
_ => ErrorKind::Other,
152+
_ => ErrorKind::Uncategorized,
153153
}
154154
}
155155

library/std/src/sys/hermit/net.rs

+28-23
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ use crate::time::Duration;
1515
pub fn init() -> io::Result<()> {
1616
if abi::network_init() < 0 {
1717
return Err(io::Error::new_const(
18-
ErrorKind::Other,
18+
ErrorKind::Uncategorized,
1919
&"Unable to initialize network interface",
2020
));
2121
}
@@ -51,7 +51,7 @@ impl TcpStream {
5151
match abi::tcpstream::connect(addr.ip().to_string().as_bytes(), addr.port(), None) {
5252
Ok(handle) => Ok(TcpStream(Arc::new(Socket(handle)))),
5353
_ => Err(io::Error::new_const(
54-
ErrorKind::Other,
54+
ErrorKind::Uncategorized,
5555
&"Unable to initiate a connection on a socket",
5656
)),
5757
}
@@ -65,44 +65,46 @@ impl TcpStream {
6565
) {
6666
Ok(handle) => Ok(TcpStream(Arc::new(Socket(handle)))),
6767
_ => Err(io::Error::new_const(
68-
ErrorKind::Other,
68+
ErrorKind::Uncategorized,
6969
&"Unable to initiate a connection on a socket",
7070
)),
7171
}
7272
}
7373

7474
pub fn set_read_timeout(&self, duration: Option<Duration>) -> io::Result<()> {
7575
abi::tcpstream::set_read_timeout(*self.0.as_inner(), duration.map(|d| d.as_millis() as u64))
76-
.map_err(|_| io::Error::new_const(ErrorKind::Other, &"Unable to set timeout value"))
76+
.map_err(|_| {
77+
io::Error::new_const(ErrorKind::Uncategorized, &"Unable to set timeout value")
78+
})
7779
}
7880

7981
pub fn set_write_timeout(&self, duration: Option<Duration>) -> io::Result<()> {
8082
abi::tcpstream::set_write_timeout(
8183
*self.0.as_inner(),
8284
duration.map(|d| d.as_millis() as u64),
8385
)
84-
.map_err(|_| io::Error::new_const(ErrorKind::Other, &"Unable to set timeout value"))
86+
.map_err(|_| io::Error::new_const(ErrorKind::Uncategorized, &"Unable to set timeout value"))
8587
}
8688

8789
pub fn read_timeout(&self) -> io::Result<Option<Duration>> {
8890
let duration = abi::tcpstream::get_read_timeout(*self.0.as_inner()).map_err(|_| {
89-
io::Error::new_const(ErrorKind::Other, &"Unable to determine timeout value")
91+
io::Error::new_const(ErrorKind::Uncategorized, &"Unable to determine timeout value")
9092
})?;
9193

9294
Ok(duration.map(|d| Duration::from_millis(d)))
9395
}
9496

9597
pub fn write_timeout(&self) -> io::Result<Option<Duration>> {
9698
let duration = abi::tcpstream::get_write_timeout(*self.0.as_inner()).map_err(|_| {
97-
io::Error::new_const(ErrorKind::Other, &"Unable to determine timeout value")
99+
io::Error::new_const(ErrorKind::Uncategorized, &"Unable to determine timeout value")
98100
})?;
99101

100102
Ok(duration.map(|d| Duration::from_millis(d)))
101103
}
102104

103105
pub fn peek(&self, buf: &mut [u8]) -> io::Result<usize> {
104106
abi::tcpstream::peek(*self.0.as_inner(), buf)
105-
.map_err(|_| io::Error::new_const(ErrorKind::Other, &"set_nodelay failed"))
107+
.map_err(|_| io::Error::new_const(ErrorKind::Uncategorized, &"peek failed"))
106108
}
107109

108110
pub fn read(&self, buffer: &mut [u8]) -> io::Result<usize> {
@@ -113,8 +115,9 @@ impl TcpStream {
113115
let mut size: usize = 0;
114116

115117
for i in ioslice.iter_mut() {
116-
let ret = abi::tcpstream::read(*self.0.as_inner(), &mut i[0..])
117-
.map_err(|_| io::Error::new_const(ErrorKind::Other, &"Unable to read on socket"))?;
118+
let ret = abi::tcpstream::read(*self.0.as_inner(), &mut i[0..]).map_err(|_| {
119+
io::Error::new_const(ErrorKind::Uncategorized, &"Unable to read on socket")
120+
})?;
118121

119122
if ret != 0 {
120123
size += ret;
@@ -138,7 +141,7 @@ impl TcpStream {
138141

139142
for i in ioslice.iter() {
140143
size += abi::tcpstream::write(*self.0.as_inner(), i).map_err(|_| {
141-
io::Error::new_const(ErrorKind::Other, &"Unable to write on socket")
144+
io::Error::new_const(ErrorKind::Uncategorized, &"Unable to write on socket")
142145
})?;
143146
}
144147

@@ -152,13 +155,13 @@ impl TcpStream {
152155

153156
pub fn peer_addr(&self) -> io::Result<SocketAddr> {
154157
let (ipaddr, port) = abi::tcpstream::peer_addr(*self.0.as_inner())
155-
.map_err(|_| io::Error::new_const(ErrorKind::Other, &"peer_addr failed"))?;
158+
.map_err(|_| io::Error::new_const(ErrorKind::Uncategorized, &"peer_addr failed"))?;
156159

157160
let saddr = match ipaddr {
158161
Ipv4(ref addr) => SocketAddr::new(IpAddr::V4(Ipv4Addr::from(addr.0)), port),
159162
Ipv6(ref addr) => SocketAddr::new(IpAddr::V6(Ipv6Addr::from(addr.0)), port),
160163
_ => {
161-
return Err(io::Error::new_const(ErrorKind::Other, &"peer_addr failed"));
164+
return Err(io::Error::new_const(ErrorKind::Uncategorized, &"peer_addr failed"));
162165
}
163166
};
164167

@@ -170,8 +173,9 @@ impl TcpStream {
170173
}
171174

172175
pub fn shutdown(&self, how: Shutdown) -> io::Result<()> {
173-
abi::tcpstream::shutdown(*self.0.as_inner(), how as i32)
174-
.map_err(|_| io::Error::new_const(ErrorKind::Other, &"unable to shutdown socket"))
176+
abi::tcpstream::shutdown(*self.0.as_inner(), how as i32).map_err(|_| {
177+
io::Error::new_const(ErrorKind::Uncategorized, &"unable to shutdown socket")
178+
})
175179
}
176180

177181
pub fn duplicate(&self) -> io::Result<TcpStream> {
@@ -180,31 +184,32 @@ impl TcpStream {
180184

181185
pub fn set_nodelay(&self, mode: bool) -> io::Result<()> {
182186
abi::tcpstream::set_nodelay(*self.0.as_inner(), mode)
183-
.map_err(|_| io::Error::new_const(ErrorKind::Other, &"set_nodelay failed"))
187+
.map_err(|_| io::Error::new_const(ErrorKind::Uncategorized, &"set_nodelay failed"))
184188
}
185189

186190
pub fn nodelay(&self) -> io::Result<bool> {
187191
abi::tcpstream::nodelay(*self.0.as_inner())
188-
.map_err(|_| io::Error::new_const(ErrorKind::Other, &"nodelay failed"))
192+
.map_err(|_| io::Error::new_const(ErrorKind::Uncategorized, &"nodelay failed"))
189193
}
190194

191195
pub fn set_ttl(&self, tll: u32) -> io::Result<()> {
192196
abi::tcpstream::set_tll(*self.0.as_inner(), tll)
193-
.map_err(|_| io::Error::new_const(ErrorKind::Other, &"unable to set TTL"))
197+
.map_err(|_| io::Error::new_const(ErrorKind::Uncategorized, &"unable to set TTL"))
194198
}
195199

196200
pub fn ttl(&self) -> io::Result<u32> {
197201
abi::tcpstream::get_tll(*self.0.as_inner())
198-
.map_err(|_| io::Error::new_const(ErrorKind::Other, &"unable to get TTL"))
202+
.map_err(|_| io::Error::new_const(ErrorKind::Uncategorized, &"unable to get TTL"))
199203
}
200204

201205
pub fn take_error(&self) -> io::Result<Option<io::Error>> {
202206
unsupported()
203207
}
204208

205209
pub fn set_nonblocking(&self, mode: bool) -> io::Result<()> {
206-
abi::tcpstream::set_nonblocking(*self.0.as_inner(), mode)
207-
.map_err(|_| io::Error::new_const(ErrorKind::Other, &"unable to set blocking mode"))
210+
abi::tcpstream::set_nonblocking(*self.0.as_inner(), mode).map_err(|_| {
211+
io::Error::new_const(ErrorKind::Uncategorized, &"unable to set blocking mode")
212+
})
208213
}
209214
}
210215

@@ -230,12 +235,12 @@ impl TcpListener {
230235

231236
pub fn accept(&self) -> io::Result<(TcpStream, SocketAddr)> {
232237
let (handle, ipaddr, port) = abi::tcplistener::accept(self.0.port())
233-
.map_err(|_| io::Error::new_const(ErrorKind::Other, &"accept failed"))?;
238+
.map_err(|_| io::Error::new_const(ErrorKind::Uncategorized, &"accept failed"))?;
234239
let saddr = match ipaddr {
235240
Ipv4(ref addr) => SocketAddr::new(IpAddr::V4(Ipv4Addr::from(addr.0)), port),
236241
Ipv6(ref addr) => SocketAddr::new(IpAddr::V6(Ipv6Addr::from(addr.0)), port),
237242
_ => {
238-
return Err(io::Error::new_const(ErrorKind::Other, &"accept failed"));
243+
return Err(io::Error::new_const(ErrorKind::Uncategorized, &"accept failed"));
239244
}
240245
};
241246

library/std/src/sys/hermit/stdio.rs

+4-4
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ impl io::Write for Stdout {
4040
unsafe { len = abi::write(1, data.as_ptr() as *const u8, data.len()) }
4141

4242
if len < 0 {
43-
Err(io::Error::new_const(io::ErrorKind::Other, &"Stdout is not able to print"))
43+
Err(io::Error::new_const(io::ErrorKind::Uncategorized, &"Stdout is not able to print"))
4444
} else {
4545
Ok(len as usize)
4646
}
@@ -52,7 +52,7 @@ impl io::Write for Stdout {
5252
unsafe { len = abi::write(1, data.as_ptr() as *const u8, data.len()) }
5353

5454
if len < 0 {
55-
Err(io::Error::new_const(io::ErrorKind::Other, &"Stdout is not able to print"))
55+
Err(io::Error::new_const(io::ErrorKind::Uncategorized, &"Stdout is not able to print"))
5656
} else {
5757
Ok(len as usize)
5858
}
@@ -81,7 +81,7 @@ impl io::Write for Stderr {
8181
unsafe { len = abi::write(2, data.as_ptr() as *const u8, data.len()) }
8282

8383
if len < 0 {
84-
Err(io::Error::new_const(io::ErrorKind::Other, &"Stderr is not able to print"))
84+
Err(io::Error::new_const(io::ErrorKind::Uncategorized, &"Stderr is not able to print"))
8585
} else {
8686
Ok(len as usize)
8787
}
@@ -93,7 +93,7 @@ impl io::Write for Stderr {
9393
unsafe { len = abi::write(2, data.as_ptr() as *const u8, data.len()) }
9494

9595
if len < 0 {
96-
Err(io::Error::new_const(io::ErrorKind::Other, &"Stderr is not able to print"))
96+
Err(io::Error::new_const(io::ErrorKind::Uncategorized, &"Stderr is not able to print"))
9797
} else {
9898
Ok(len as usize)
9999
}

library/std/src/sys/hermit/thread.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ impl Thread {
3939
// The thread failed to start and as a result p was not consumed. Therefore, it is
4040
// safe to reconstruct the box so that it gets deallocated.
4141
drop(Box::from_raw(p));
42-
Err(io::Error::new_const(io::ErrorKind::Other, &"Unable to create thread!"))
42+
Err(io::Error::new_const(io::ErrorKind::Uncategorized, &"Unable to create thread!"))
4343
} else {
4444
Ok(Thread { tid: tid })
4545
};

0 commit comments

Comments
 (0)