]>
Commit | Line | Data |
---|---|---|
c34b1796 AL |
1 | // Copyright 2014 The Rust Project Developers. See the COPYRIGHT |
2 | // file at the top-level directory of this distribution and at | |
3 | // http://rust-lang.org/COPYRIGHT. | |
4 | // | |
5 | // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or | |
6 | // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license | |
7 | // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your | |
8 | // option. This file may not be copied, modified, or distributed | |
9 | // except according to those terms. | |
10 | ||
11 | //! Traits for working with Errors. | |
12 | //! | |
13 | //! # The `Error` trait | |
14 | //! | |
15 | //! `Error` is a trait representing the basic expectations for error values, | |
16 | //! i.e. values of type `E` in `Result<T, E>`. At a minimum, errors must provide | |
17 | //! a description, but they may optionally provide additional detail (via | |
18 | //! `Display`) and cause chain information: | |
19 | //! | |
20 | //! ``` | |
21 | //! use std::fmt::Display; | |
22 | //! | |
23 | //! trait Error: Display { | |
24 | //! fn description(&self) -> &str; | |
25 | //! | |
26 | //! fn cause(&self) -> Option<&Error> { None } | |
27 | //! } | |
28 | //! ``` | |
29 | //! | |
30 | //! The `cause` method is generally used when errors cross "abstraction | |
31 | //! boundaries", i.e. when a one module must report an error that is "caused" | |
32 | //! by an error from a lower-level module. This setup makes it possible for the | |
33 | //! high-level module to provide its own errors that do not commit to any | |
34 | //! particular implementation, but also reveal some of its implementation for | |
35 | //! debugging via `cause` chains. | |
36 | ||
37 | #![stable(feature = "rust1", since = "1.0.0")] | |
38 | ||
39 | // A note about crates and the facade: | |
40 | // | |
41 | // Originally, the `Error` trait was defined in libcore, and the impls | |
42 | // were scattered about. However, coherence objected to this | |
43 | // arrangement, because to create the blanket impls for `Box` required | |
44 | // knowing that `&str: !Error`, and we have no means to deal with that | |
45 | // sort of conflict just now. Therefore, for the time being, we have | |
46 | // moved the `Error` trait into libstd. As we evolve a sol'n to the | |
47 | // coherence challenge (e.g., specialization, neg impls, etc) we can | |
48 | // reconsider what crate these items belong in. | |
49 | ||
bd371182 | 50 | use any::TypeId; |
62682a34 | 51 | use boxed::Box; |
c34b1796 AL |
52 | use convert::From; |
53 | use fmt::{self, Debug, Display}; | |
bd371182 AL |
54 | use marker::{Send, Sync, Reflect}; |
55 | use mem::transmute; | |
c34b1796 | 56 | use num; |
bd371182 AL |
57 | use option::Option::{self, Some, None}; |
58 | use result::Result::{self, Ok, Err}; | |
59 | use raw::TraitObject; | |
c34b1796 AL |
60 | use str; |
61 | use string::{self, String}; | |
62 | ||
63 | /// Base functionality for all errors in Rust. | |
64 | #[stable(feature = "rust1", since = "1.0.0")] | |
bd371182 | 65 | pub trait Error: Debug + Display + Reflect { |
c34b1796 AL |
66 | /// A short description of the error. |
67 | /// | |
68 | /// The description should not contain newlines or sentence-ending | |
69 | /// punctuation, to facilitate embedding in larger user-facing | |
70 | /// strings. | |
71 | #[stable(feature = "rust1", since = "1.0.0")] | |
72 | fn description(&self) -> &str; | |
73 | ||
74 | /// The lower-level cause of this error, if any. | |
75 | #[stable(feature = "rust1", since = "1.0.0")] | |
76 | fn cause(&self) -> Option<&Error> { None } | |
bd371182 AL |
77 | |
78 | /// Get the `TypeId` of `self` | |
79 | #[doc(hidden)] | |
62682a34 | 80 | #[unstable(feature = "error_type_id", |
bd371182 AL |
81 | reason = "unclear whether to commit to this public implementation detail")] |
82 | fn type_id(&self) -> TypeId where Self: 'static { | |
83 | TypeId::of::<Self>() | |
84 | } | |
c34b1796 AL |
85 | } |
86 | ||
87 | #[stable(feature = "rust1", since = "1.0.0")] | |
88 | impl<'a, E: Error + 'a> From<E> for Box<Error + 'a> { | |
89 | fn from(err: E) -> Box<Error + 'a> { | |
90 | Box::new(err) | |
91 | } | |
92 | } | |
93 | ||
94 | #[stable(feature = "rust1", since = "1.0.0")] | |
9346a6ac AL |
95 | impl<'a, E: Error + Send + Sync + 'a> From<E> for Box<Error + Send + Sync + 'a> { |
96 | fn from(err: E) -> Box<Error + Send + Sync + 'a> { | |
c34b1796 AL |
97 | Box::new(err) |
98 | } | |
99 | } | |
100 | ||
101 | #[stable(feature = "rust1", since = "1.0.0")] | |
9346a6ac AL |
102 | impl From<String> for Box<Error + Send + Sync> { |
103 | fn from(err: String) -> Box<Error + Send + Sync> { | |
c34b1796 AL |
104 | #[derive(Debug)] |
105 | struct StringError(String); | |
106 | ||
107 | impl Error for StringError { | |
108 | fn description(&self) -> &str { &self.0 } | |
109 | } | |
110 | ||
111 | impl Display for StringError { | |
112 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { | |
113 | Display::fmt(&self.0, f) | |
114 | } | |
115 | } | |
116 | ||
9346a6ac AL |
117 | Box::new(StringError(err)) |
118 | } | |
119 | } | |
120 | ||
121 | #[stable(feature = "rust1", since = "1.0.0")] | |
122 | impl<'a, 'b> From<&'b str> for Box<Error + Send + Sync + 'a> { | |
123 | fn from(err: &'b str) -> Box<Error + Send + Sync + 'a> { | |
62682a34 | 124 | From::from(String::from(err)) |
c34b1796 AL |
125 | } |
126 | } | |
127 | ||
128 | #[stable(feature = "rust1", since = "1.0.0")] | |
129 | impl Error for str::ParseBoolError { | |
130 | fn description(&self) -> &str { "failed to parse bool" } | |
131 | } | |
132 | ||
133 | #[stable(feature = "rust1", since = "1.0.0")] | |
134 | impl Error for str::Utf8Error { | |
135 | fn description(&self) -> &str { | |
9346a6ac | 136 | "invalid utf-8: corrupt contents" |
c34b1796 AL |
137 | } |
138 | } | |
139 | ||
140 | #[stable(feature = "rust1", since = "1.0.0")] | |
141 | impl Error for num::ParseIntError { | |
142 | fn description(&self) -> &str { | |
62682a34 | 143 | self.__description() |
c34b1796 AL |
144 | } |
145 | } | |
146 | ||
147 | #[stable(feature = "rust1", since = "1.0.0")] | |
148 | impl Error for num::ParseFloatError { | |
149 | fn description(&self) -> &str { | |
d9579d0f | 150 | self.__description() |
c34b1796 AL |
151 | } |
152 | } | |
153 | ||
154 | #[stable(feature = "rust1", since = "1.0.0")] | |
155 | impl Error for string::FromUtf8Error { | |
156 | fn description(&self) -> &str { | |
157 | "invalid utf-8" | |
158 | } | |
159 | } | |
160 | ||
161 | #[stable(feature = "rust1", since = "1.0.0")] | |
162 | impl Error for string::FromUtf16Error { | |
163 | fn description(&self) -> &str { | |
164 | "invalid utf-16" | |
165 | } | |
166 | } | |
167 | ||
bd371182 AL |
168 | // copied from any.rs |
169 | impl Error + 'static { | |
170 | /// Returns true if the boxed type is the same as `T` | |
c1a9b12d | 171 | #[stable(feature = "error_downcast", since = "1.3.0")] |
bd371182 AL |
172 | #[inline] |
173 | pub fn is<T: Error + 'static>(&self) -> bool { | |
174 | // Get TypeId of the type this function is instantiated with | |
175 | let t = TypeId::of::<T>(); | |
176 | ||
177 | // Get TypeId of the type in the trait object | |
178 | let boxed = self.type_id(); | |
179 | ||
180 | // Compare both TypeIds on equality | |
181 | t == boxed | |
182 | } | |
183 | ||
184 | /// Returns some reference to the boxed value if it is of type `T`, or | |
185 | /// `None` if it isn't. | |
c1a9b12d | 186 | #[stable(feature = "error_downcast", since = "1.3.0")] |
bd371182 AL |
187 | #[inline] |
188 | pub fn downcast_ref<T: Error + 'static>(&self) -> Option<&T> { | |
189 | if self.is::<T>() { | |
190 | unsafe { | |
191 | // Get the raw representation of the trait object | |
192 | let to: TraitObject = transmute(self); | |
193 | ||
194 | // Extract the data pointer | |
195 | Some(transmute(to.data)) | |
196 | } | |
197 | } else { | |
198 | None | |
199 | } | |
200 | } | |
201 | ||
202 | /// Returns some mutable reference to the boxed value if it is of type `T`, or | |
203 | /// `None` if it isn't. | |
c1a9b12d | 204 | #[stable(feature = "error_downcast", since = "1.3.0")] |
bd371182 AL |
205 | #[inline] |
206 | pub fn downcast_mut<T: Error + 'static>(&mut self) -> Option<&mut T> { | |
207 | if self.is::<T>() { | |
208 | unsafe { | |
209 | // Get the raw representation of the trait object | |
210 | let to: TraitObject = transmute(self); | |
211 | ||
212 | // Extract the data pointer | |
213 | Some(transmute(to.data)) | |
214 | } | |
215 | } else { | |
216 | None | |
217 | } | |
218 | } | |
219 | } | |
220 | ||
221 | impl Error + 'static + Send { | |
222 | /// Forwards to the method defined on the type `Any`. | |
c1a9b12d | 223 | #[stable(feature = "error_downcast", since = "1.3.0")] |
bd371182 AL |
224 | #[inline] |
225 | pub fn is<T: Error + 'static>(&self) -> bool { | |
226 | <Error + 'static>::is::<T>(self) | |
227 | } | |
228 | ||
229 | /// Forwards to the method defined on the type `Any`. | |
c1a9b12d | 230 | #[stable(feature = "error_downcast", since = "1.3.0")] |
bd371182 AL |
231 | #[inline] |
232 | pub fn downcast_ref<T: Error + 'static>(&self) -> Option<&T> { | |
233 | <Error + 'static>::downcast_ref::<T>(self) | |
234 | } | |
235 | ||
236 | /// Forwards to the method defined on the type `Any`. | |
c1a9b12d SL |
237 | #[stable(feature = "error_downcast", since = "1.3.0")] |
238 | #[inline] | |
239 | pub fn downcast_mut<T: Error + 'static>(&mut self) -> Option<&mut T> { | |
240 | <Error + 'static>::downcast_mut::<T>(self) | |
241 | } | |
242 | } | |
243 | ||
244 | impl Error + 'static + Send + Sync { | |
245 | /// Forwards to the method defined on the type `Any`. | |
246 | #[stable(feature = "error_downcast", since = "1.3.0")] | |
247 | #[inline] | |
248 | pub fn is<T: Error + 'static>(&self) -> bool { | |
249 | <Error + 'static>::is::<T>(self) | |
250 | } | |
251 | ||
252 | /// Forwards to the method defined on the type `Any`. | |
253 | #[stable(feature = "error_downcast", since = "1.3.0")] | |
254 | #[inline] | |
255 | pub fn downcast_ref<T: Error + 'static>(&self) -> Option<&T> { | |
256 | <Error + 'static>::downcast_ref::<T>(self) | |
257 | } | |
258 | ||
259 | /// Forwards to the method defined on the type `Any`. | |
260 | #[stable(feature = "error_downcast", since = "1.3.0")] | |
bd371182 AL |
261 | #[inline] |
262 | pub fn downcast_mut<T: Error + 'static>(&mut self) -> Option<&mut T> { | |
263 | <Error + 'static>::downcast_mut::<T>(self) | |
264 | } | |
265 | } | |
266 | ||
267 | impl Error { | |
268 | #[inline] | |
c1a9b12d | 269 | #[stable(feature = "error_downcast", since = "1.3.0")] |
bd371182 AL |
270 | /// Attempt to downcast the box to a concrete type. |
271 | pub fn downcast<T: Error + 'static>(self: Box<Self>) -> Result<Box<T>, Box<Error>> { | |
272 | if self.is::<T>() { | |
273 | unsafe { | |
274 | // Get the raw representation of the trait object | |
62682a34 | 275 | let raw = Box::into_raw(self); |
bd371182 AL |
276 | let to: TraitObject = |
277 | transmute::<*mut Error, TraitObject>(raw); | |
278 | ||
279 | // Extract the data pointer | |
280 | Ok(Box::from_raw(to.data as *mut T)) | |
281 | } | |
282 | } else { | |
283 | Err(self) | |
284 | } | |
285 | } | |
286 | } | |
287 | ||
288 | impl Error + Send { | |
289 | #[inline] | |
c1a9b12d | 290 | #[stable(feature = "error_downcast", since = "1.3.0")] |
bd371182 | 291 | /// Attempt to downcast the box to a concrete type. |
c1a9b12d SL |
292 | pub fn downcast<T: Error + 'static>(self: Box<Self>) |
293 | -> Result<Box<T>, Box<Error + Send>> { | |
bd371182 AL |
294 | let err: Box<Error> = self; |
295 | <Error>::downcast(err).map_err(|s| unsafe { | |
296 | // reapply the Send marker | |
297 | transmute::<Box<Error>, Box<Error + Send>>(s) | |
298 | }) | |
299 | } | |
300 | } | |
c1a9b12d SL |
301 | |
302 | impl Error + Send + Sync { | |
303 | #[inline] | |
304 | #[stable(feature = "error_downcast", since = "1.3.0")] | |
305 | /// Attempt to downcast the box to a concrete type. | |
306 | pub fn downcast<T: Error + 'static>(self: Box<Self>) | |
307 | -> Result<Box<T>, Box<Self>> { | |
308 | let err: Box<Error> = self; | |
309 | <Error>::downcast(err).map_err(|s| unsafe { | |
310 | // reapply the Send+Sync marker | |
311 | transmute::<Box<Error>, Box<Error + Send + Sync>>(s) | |
312 | }) | |
313 | } | |
314 | } | |
315 | ||
316 | #[cfg(test)] | |
317 | mod tests { | |
318 | use prelude::v1::*; | |
319 | use super::Error; | |
320 | use fmt; | |
321 | ||
322 | #[derive(Debug, PartialEq)] | |
323 | struct A; | |
324 | #[derive(Debug, PartialEq)] | |
325 | struct B; | |
326 | ||
327 | impl fmt::Display for A { | |
328 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { | |
329 | write!(f, "A") | |
330 | } | |
331 | } | |
332 | impl fmt::Display for B { | |
333 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { | |
334 | write!(f, "B") | |
335 | } | |
336 | } | |
337 | ||
338 | impl Error for A { | |
339 | fn description(&self) -> &str { "A-desc" } | |
340 | } | |
341 | impl Error for B { | |
342 | fn description(&self) -> &str { "A-desc" } | |
343 | } | |
344 | ||
345 | #[test] | |
346 | fn downcasting() { | |
347 | let mut a = A; | |
348 | let mut a = &mut a as &mut (Error + 'static); | |
349 | assert_eq!(a.downcast_ref::<A>(), Some(&A)); | |
350 | assert_eq!(a.downcast_ref::<B>(), None); | |
351 | assert_eq!(a.downcast_mut::<A>(), Some(&mut A)); | |
352 | assert_eq!(a.downcast_mut::<B>(), None); | |
353 | ||
354 | let a: Box<Error> = Box::new(A); | |
355 | match a.downcast::<B>() { | |
356 | Ok(..) => panic!("expected error"), | |
357 | Err(e) => assert_eq!(*e.downcast::<A>().unwrap(), A), | |
358 | } | |
359 | } | |
360 | } |