首页 > 解决方案 > Unable to satisfy Into trait bound

问题描述

For some reason I couldn't figure anything out from searching the web.

So I have a type synonym using the libc crate (a simple wrapper):

pub type SyscallResult<R: Into<i64>> = Result<R, c_int>;

The point of having the Into<i64> is because some syscalls have i32 or i64 results but errno is always an i32. (c_int -> i32)

There was no problems using concrete types provided by std in the function signature.

pub fn handle_syscall_result(result: SyscallResult<i64>) -> i64
{
    match result {
        Ok(status: i64) => status,
        Err(errno: c_int) => {
            let err = unsafe { CStr::from_ptr(libc::strerror(errno)) };
            println!("{:?}", err);
            errno as i64
        }
    }
}

But when I try to propagate the trait bound into the signature of a function, things start to go haywire:

fn new_syscall_result<T>(status: Option<T>) -> SyscallResult<i64>
where
    T: Into::<i64>,
{
    let errno: c_int = errno();

    match errno {
        -1 => Err(errno),
        _ => Ok(status.unwrap_or(errno)),
    }
}

Gives

error[E0308]: mismatched types
  --> 
   |
35 | fn new_syscall_result<T>(status: Option<T>) -> SyscallResult<i64>
   |                       - this type parameter
...
43 |         _ => Ok(status.unwrap_or(errno)),
   |                 ^^^^^^^^^^^^^^^^^^^^^^^ expected `i64`, found type parameter `T`
   |
   = note:        expected type `i64`
           found type parameter `T`

which makes it look like it is doing the right thing, as the i32 was casted to an i64, yet for some reason it doesn't satisfy the trait bound T?

Unwrapping Option<T> should naturally give T and from what I've read from the std::convert::From docs, Into<i64> should be implemented for i64 as well (reflexivity), though even if it wasn't casted From<i32> for i64 is a valid implementation which means Into<i64> for i32 should exist.

Switching the return type to SyscallResult<T> doesn't make a difference either

error[E0308]: mismatched types
  --> 
   |
35 | fn new_syscall_result<T>(status: Option<T>) -> SyscallResult<T>
   |                       - this type parameter
...
43 |         _ => Ok(status.unwrap_or(errno)),
   |                                  ^^^^^ expected type parameter `T`, found `i32`
   |
   = note: expected type parameter `T`
                        found type `i32`

标签: rusttraits

解决方案


You need to explicitly call into():

        _ => Ok(status.map(Into::<i64>::into).unwrap_or(errno.into())),
//                     ^^^^^^^^^^^^^^^^^^^^^^

status is a Option<T>, bot you want an Option<i64>. To achieve this, we transform the type contained in the Option by using Option::map.

There is another problem in your code:

        _ => Ok(status.map(Into::<i64>::into).unwrap_or(errno.into())),
//                                                      ^^^^^^^^^^^^

errno is a c_int, so on most platforms a i32, but we need an i64. Again, into() must be used to transform the type. Explicit type annotations are not necessary here, the compiler can deduce the correct type from context. Alternatively, you could also use a cast here:

        _ => Ok(status.map(Into::<i64>::into).unwrap_or(errno as i64)),

推荐阅读