Description
Deref coercions are tricky. &x
will coerce to &***x
if used in a place that expects a value of the type of &***x
, however, this does not work for trait-bound generics.
The example that came up recently was:
use std::net::TcpListener;
let owned = "foobar".to_string(); // owned string came from somewhere
let works = TcpListener::bind("some string");
let doesn't = TcpListener::bind(&owned);
This errors with
error[E0277]: the trait bound `std::string::String: std::net::ToSocketAddrs` is not satisfied
--> <anon>:5:18
|
5 | let listen = TcpListener::bind(&owned);
| ^^^^^^^^^^^^^^^^^ the trait `std::net::ToSocketAddrs` is not implemented for `std::string::String`
|
= note: required because of the requirements on the impl of `std::net::ToSocketAddrs` for `&std::string::String`
= note: required by `std::net::TcpListener::bind`
because while &owned
would usually coerce to an &str
, in this case it doesn't because even though TcpListener::bind
would accept a &str
, it doesn't accept only that type and the deref coercion won't work.
Programmers should not have to know these details to get their code to work. It would be nice if deref coercions worked in places where a generic bound type is expected, but there are other issues there (what happens if the trait gets implemented on String
?) so it's not a clear win and would need an rfc anyway.
At the very least, we should suggest the usage of a dereference operator here.
The heuristics are:
- you have a trait bound error
- The error is on a reference expression
&x
- The expression is used in one of the allowed places for deref coercions (may not be necessary, really)
- The trait isn't implemented by
&x
- The trait is implemented by
&***x
for some amount of*
operations
then, suggest the correct thing with the dereference operators.