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.  One important
> difference is that these propagation methods *panic* if *errp is NULL,
> unlike error_propagate() which eats subsequent errors[1].  The reason
> for this is that in C you have an error_set*() call at the site where
> the error is created, and calls to error_propagate() are relatively rare.
>
> In Rust instead, even though these functions do "propagate" a
> qemu_api::Error into a C Error**, there is no error_setg() anywhere that
> could check for non-NULL errp and call abort().  error_propagate()'s
> behavior of ignoring subsequent errors is generally considered weird,
> and there would be a bigger risk of triggering it from Rust code.
>
> [1] This is actually a violation of the preconditions of error_propagate(),
>     so it should not happen.  But you never know...
>
> 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..80157f6ea1b
> --- /dev/null
> +++ b/rust/qemu-api/src/error.rs

[...]

> +    /// Equivalent of the C function `error_propagate`.  Fill `*errp`
> +    /// with the information container in `self` if `errp` is not NULL;
> +    /// then consume it.
> +    ///
> +    /// This is similar to the C API `error_propagate`, but it panics if
> +    /// `*errp` is not `NULL`.
> +    ///
> +    /// # Safety
> +    ///
> +    /// `errp` must be a valid argument to `error_propagate`; it can be
> +    /// `NULL` or it can point to any of:
> +    /// * `error_abort`
> +    /// * `error_fatal`
> +    /// * a local variable of (C) type `Error *`

This local variable must contain NULL.

> +    ///
> +    /// Typically `errp` is received from C code and need not be
> +    /// checked further at the Rust↔C boundary.
> +    pub unsafe fn propagate(self, errp: *mut *mut bindings::Error) {
> +        if errp.is_null() {
> +            return;
> +        }
> +
> +        // SAFETY: caller guarantees that errp and *errp are valid
> +        unsafe {
> +            assert_eq!(*errp, ptr::null_mut());
> +            bindings::error_propagate(errp, self.clone_to_foreign_ptr());
> +        }
> +    }

[...]

With the comment tightened:
Reviewed-by: Markus Armbruster <arm...@redhat.com>

The commit message and comment improvements are lovely!


Reply via email to