On 6/2/25 15:18, Markus Armbruster wrote:
Paolo Bonzini <pbonz...@redhat.com> writes:
Provide an implementation of std::error::Error that bridges the Rust
anyhow::Error and std::panic::Location types with QEMU's Error*.
It also has several utility methods, analogous to error_propagate(),
that convert a Result into a return value + Error** pair.
Signed-off-by: Paolo Bonzini <pbonz...@redhat.com>
[...]
diff --git a/rust/qemu-api/src/error.rs b/rust/qemu-api/src/error.rs
new file mode 100644
index 00000000000..0bdd413a0a2
--- /dev/null
+++ b/rust/qemu-api/src/error.rs
@@ -0,0 +1,299 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+//! Error propagation for QEMU Rust code
+//!
+//! In QEMU, an `Error` usually consists of a message and an errno value.
Uh, it actually consists of a message and an ErrorClass value.
You completely ignore ErrorClass in your Rust interface. I approve.
There are convenience functions that accept an errno, but they don't
store the errno in the Error struct, they append ": " and
strerror(errno) to the message. Same for Windows GetLastError() values.
Good point - bad wording choice on my part.
I was referring exactly to the construction: whereas C constructs an
Error (for convenience) from a message and an errno, Rust replaces the
errno with an anyhow::Error. The function however is the same, namely
to include the description of the error when it comes from code that
doesn't speak Error*.
+//! In this wrapper, the errno value is replaced by an [`anyhow::Error`]
I'm not sure the anyhow::Error replaces anything. It's simply the
bridge to idiomatic Rust errors.
And errno is the bridge to "idiomatic" C errors. :) But I agree that it
should not be mentioned in the first sentence.
+//! Note that the backtrace that is provided by `anyhow` is not used yet,
+//! only the message ends up in the QEMU `Error*`.
+//!
+//! The message part can be used to clarify the inner error, similar to
+//! `error_prepend`, and of course to describe an erroneous condition that
Clarify you're talking about C error_prepend() here?
Yes. But I'll rephrase to eliminate this reference.
+impl std::error::Error for Error {
+ fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
+ self.cause.as_ref().map(AsRef::as_ref)
+ }
+
+ #[allow(deprecated)]
+ fn description(&self) -> &str {
+ self.msg
+ .as_deref()
+ .or_else(||
self.cause.as_deref().map(std::error::Error::description))
+ .unwrap_or("unknown error")
Can "unknown error" still happen now you dropped the Default trait?
No, but I prefer to limit undocumented unwrap() to the bare minimum.
I'll use .expect() which also panics on the unexpected case, but
includes an error.
+ }
+}
+
+impl Display for Error {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ let mut prefix = "";
+ if let Some(ref msg) = self.msg {
+ write!(f, "{msg}")?;
+ prefix = ": ";
+ }
+ if let Some(ref cause) = self.cause {
+ write!(f, "{prefix}{cause}")?;
+ } else if prefix.is_empty() {
+ f.write_str("unknown error")?;
Can we still get here now you dropped the Default trait?
Same as above.
Uh, is it? Let's see...
+ /// with the information container in `self` if `errp` is not NULL;
+ /// then consume it.
+ ///
+ /// # Safety
+ ///
+ /// `errp` must be a valid argument to `error_propagate`;
Reminder for later: the valid @errp arguments for C error_propagate()
are
* NULL
* &error_abort
* &error_fatal
* Address of some Error * variable containing NULL
* Address of some Error * variable containing non-NULL
I will add this note.
What does errp.write(err) do? I *guess* it simply stores @err in @errp.
Matches neither behavior.
If that's true, then passing &error_abort or &error_fatal to Rust does
not work, and neither does error accumulation. Not equivalent of C
error_propagate().
Is "propagate" semantics what you want here?
If not, use another name.
As replied elsewhere, yes.
Paolo