3
votes
struct Foo(i32);

impl<'a> Into<i32> for &'a Foo {
    fn into(self) -> i32 {
        self.0
    }
}

fn test<I: Into<i32>>(i: I) {
    let n: i32 = i.into();
    println!("{}", n);
}

fn main() {
    let f = Foo(42);
    test(&f);
}

playground

This works but just looking at test

fn test<I: Into<i32>>(i: I) {
    let n: i32 = i.into();
    println!("{}", n);
}

The function can access both a borrow and a move/copy depending on how the Into trait is implemented.

impl<'a> Into<i32> for &'a Foo
// vs
impl Into<i32> for Foo

Now the user could try to call test like test(f); instead of test(&f); and would receive the following error message.

error[E0277]: the trait bound `i32: std::convert::From<Foo>` is not satisfied
  --> <anon>:16:5
   |
16 |     test(f);
   |     ^^^^ trait `i32: std::convert::From<Foo>` not satisfied
   |

Would it be possible to always force a borrow? Something similar to this

fn test<I: Into<i32>>(i: &I) {
    let n: i32 = i.into();
    println!("{}", n);
}

So that the user would get an error message similar to, "Expected &XX but found YY".

1
I am a bit confused on what you're trying to achieve here. Do you want to change the compiler's error message under that situation? Also note that you should preferably impl From instead of Into. - E_net4 the curator
@E_net4 A clearer error message. - Maik Klein

1 Answers

0
votes

A where-clause works here by specifying the lifetime:

fn test<'a, I>(i: &'a I) where &'a I: Into<i32> {
    let n: i32 = i.into();
    println!("{}", n);
}

Now when you attempt to build with test(f);, the message is clearer:

error[E0308]: mismatched types
  --> src/main.rs:16:10
   |
16 |     test(f);
   |          ^
   |          |
   |          expected `&Foo`, found struct `Foo`
   |          help: consider borrowing here: `&f`