Repository: dtolnay/cxx (language: rust)

Issue:
Can't return Result<Box<T>>
I am trying to return a Result<Box< T >> and I am getting the below compile errors. 
>expected struct `std::boxed::Box`, found enum `std::result::Result`
>expected enum `std::result::Result`, found *-ptr

I have tried setting the return type to Result<T> and Box<T> and both compile fine.

Here is a simple proof. 

```rust
#[cxx::bridge()]
mod test {
    struct Test {
        size: usize,
    }

    extern "Rust" {
        fn test() -> Result<Box<Test>>;
    }   
}       
        
fn test() -> Result<Box<test::Test>> {
    Ok(Box::<test::Test>::new(test::Test{size: 0}))
}
```

The reason I want to return a Result<Box<T>> is because in my use case T is a rust opaque type, but I made T a shared type to check that both Result<T> and Box<T> compile fine where Result<Box<T>> doesn't.


Discussion / hints:

Fix patch:
diff --git a/gen/src/write.rs b/gen/src/write.rs
index 862be814f..2e2b81f8c 100644
--- a/gen/src/write.rs
+++ b/gen/src/write.rs
@@ -775,9 +775,11 @@ fn write_rust_function_shim_impl(
         write!(out, "extern$");
     }
     write!(out, ")");
-    if let Some(ret) = &sig.ret {
-        if let Type::RustBox(_) | Type::UniquePtr(_) = ret {
-            write!(out, ")");
+    if !indirect_return {
+        if let Some(ret) = &sig.ret {
+            if let Type::RustBox(_) | Type::UniquePtr(_) = ret {
+                write!(out, ")");
+            }
         }
     }
     writeln!(out, ";");
diff --git a/macro/src/expand.rs b/macro/src/expand.rs
index 879431e03..1c21770f2 100644
--- a/macro/src/expand.rs
+++ b/macro/src/expand.rs
@@ -580,42 +580,42 @@ fn expand_rust_function_shim_impl(
     };
     call.extend(quote! { (#(#vars),*) });
 
-    let mut expr = sig
-        .ret
-        .as_ref()
-        .and_then(|ret| match ret {
-            Type::Ident(ident) if ident == RustString => {
-                Some(quote!(::cxx::private::RustString::from(#call)))
-            }
-            Type::RustBox(_) => Some(quote!(::std::boxed::Box::into_raw(#call))),
-            Type::RustVec(vec) => {
-                if vec.inner == RustString {
-                    Some(quote!(::cxx::private::RustVec::from_vec_string(#call)))
-                } else {
-                    Some(quote!(::cxx::private::RustVec::from(#call)))
-                }
+    let conversion = sig.ret.as_ref().and_then(|ret| match ret {
+        Type::Ident(ident) if ident == RustString => Some(quote!(::cxx::private::RustString::from)),
+        Type::RustBox(_) => Some(quote!(::std::boxed::Box::into_raw)),
+        Type::RustVec(vec) => {
+            if vec.inner == RustString {
+                Some(quote!(::cxx::private::RustVec::from_vec_string))
+            } else {
+                Some(quote!(::cxx::private::RustVec::from))
             }
-            Type::UniquePtr(_) => Some(quote!(::cxx::UniquePtr::into_raw(#call))),
-            Type::Ref(ty) => match &ty.inner {
-                Type::Ident(ident) if ident == RustString => match ty.mutability {
-                    None => Some(quote!(::cxx::private::RustString::from_ref(#call))),
-                    Some(_) => Some(quote!(::cxx::private::RustString::from_mut(#call))),
-                },
-                Type::RustVec(vec) if vec.inner == RustString => match ty.mutability {
-                    None => Some(quote!(::cxx::private::RustVec::from_ref_vec_string(#call))),
-                    Some(_) => Some(quote!(::cxx::private::RustVec::from_mut_vec_string(#call))),
-                },
-                Type::RustVec(_) => match ty.mutability {
-                    None => Some(quote!(::cxx::private::RustVec::from_ref(#call))),
-                    Some(_) => Some(quote!(::cxx::private::RustVec::from_mut(#call))),
-                },
-                _ => None,
+        }
+        Type::UniquePtr(_) => Some(quote!(::cxx::UniquePtr::into_raw)),
+        Type::Ref(ty) => match &ty.inner {
+            Type::Ident(ident) if ident == RustString => match ty.mutability {
+                None => Some(quote!(::cxx::private::RustString::from_ref)),
+                Some(_) => Some(quote!(::cxx::private::RustString::from_mut)),
+            },
+            Type::RustVec(vec) if vec.inner == RustString => match ty.mutability {
+                None => Some(quote!(::cxx::private::RustVec::from_ref_vec_string)),
+                Some(_) => Some(quote!(::cxx::private::RustVec::from_mut_vec_string)),
+            },
+            Type::RustVec(_) => match ty.mutability {
+                None => Some(quote!(::cxx::private::RustVec::from_ref)),
+                Some(_) => Some(quote!(::cxx::private::RustVec::from_mut)),
             },
-            Type::Str(_) => Some(quote!(::cxx::private::RustStr::from(#call))),
-            Type::SliceRefU8(_) => Some(quote!(::cxx::private::RustSliceU8::from(#call))),
             _ => None,
-        })
-        .unwrap_or(call);
+        },
+        Type::Str(_) => Some(quote!(::cxx::private::RustStr::from)),
+        Type::SliceRefU8(_) => Some(quote!(::cxx::private::RustSliceU8::from)),
+        _ => None,
+    });
+
+    let mut expr = match conversion {
+        None => call,
+        Some(conversion) if !sig.throws => quote!(#conversion(#call)),
+        Some(conversion) => quote!(::std::result::Result::map(#call, #conversion)),
+    };
 
     let mut outparam = None;
     let indirect_return = indirect_return(sig, types);


Test patch:
diff --git a/tests/ffi/lib.rs b/tests/ffi/lib.rs
index d6724f569..2829fef51 100644
--- a/tests/ffi/lib.rs
+++ b/tests/ffi/lib.rs
@@ -152,6 +152,7 @@ pub mod ffi {
 
         fn r_try_return_void() -> Result<()>;
         fn r_try_return_primitive() -> Result<usize>;
+        fn r_try_return_box() -> Result<Box<R>>;
         fn r_fail_return_primitive() -> Result<usize>;
 
         fn r_return_r2(n: usize) -> Box<R2>;
@@ -334,6 +335,10 @@ fn r_try_return_primitive() -> Result<usize, Error> {
     Ok(2020)
 }
 
+fn r_try_return_box() -> Result<Box<R>, Error> {
+    Ok(Box::new(2020))
+}
+
 fn r_fail_return_primitive() -> Result<usize, Error> {
     Err(Error)
 }