问题
use std::sync::Arc;
trait Trait {}
struct TraitImpl {}
impl Trait for TraitImpl {}
fn main() {
let value = TraitImpl {};
let _: Arc<dyn Trait> = Arc::new(value); // compiles
let _: Arc<dyn Trait> = value.into(); // doesn't compile
}
Result:
error[E0277]: the trait bound `std::sync::Arc<dyn Trait>: std::convert::From<TraitImpl>` is not satisfied
--> src/main.rs:10:35
|
10 | let _: Arc<dyn Trait> = value.into(); // doesn't compile
| ^^^^ the trait `std::convert::From<TraitImpl>` is not implemented for `std::sync::Arc<dyn Trait>`
|
= help: the following implementations were found:
<std::sync::Arc<T> as std::convert::From<T>>
<std::sync::Arc<T> as std::convert::From<std::boxed::Box<T>>>
<std::sync::Arc<[T]> as std::convert::From<&[T]>>
<std::sync::Arc<[T]> as std::convert::From<std::vec::Vec<T>>>
and 8 others
= note: required because of the requirements on the impl of `std::convert::Into<std::sync::Arc<dyn Trait>>` for `TraitImpl`
(Playground)
Why does Arc::new(value)
compile but not value.into()
? I don't understand why Arc<T>::new() is satisfied while From<T>::from isn't.
impl<T> Arc<T> {
pub fn new(data: T) -> Arc<T>
}
impl<T> From<T> for Arc<T> {
fn from(t: T) -> Arc<T>
}
回答1:
There is a fundamental difference in your two lines. The first one:
let _: Arc<dyn Trait> = Arc::new(value);
The pattern is not important for the resolution of Arc::new()
, since it is defined as you noted:
impl<T> Arc<T> {
pub fn new(data: T) -> Arc<T>
}
So the type T
is deduced from the type of value
that is TraitImpl
, and an Arc<TraitImpl>
is created.
Then this type is implicitly unsized-coerced to that Arc<dyn Trait>
and all compiles fine.
But the second line is tricker:
let _: Arc<dyn Trait> = value.into();
Since there is not an into
function in TraitImpl
the compiler searches any trait in scope and finds Into<T>::into()
, that is defined as:
pub trait Into<T> {
fn into(self) -> T;
}
Now the compiler wonders what type would that T
be. Since it is the return of the function, it guesses that T
is Arc<dyn Trait>
. Now the only interesting implementation of Into
is in terms of From
:
impl<X, T> Into<T> for X where
T: From<X>
Here X
is TraitImpl
and T
is Arc<dyn Trait>
. If you look at the impls of Arc
for From
, it includes a lot of them, but none that applies. This is the most similar:
impl<T> From<T> for Arc<T>
Then, the compiler shows a few of the failing candidates and emits an error.
The TL;DR; is that you actually want to do two conversions: from TraitImpl
to Arc<TraitImpl>
and then from Arc<TraitImpl>
to Arc<dyn Trait>
. But you cannot do both in a single coertion, the compiler must have the intermediate type spelled out somehow.
回答2:
For all generic Rust code there is an implicit Sized
bound on any T
. This:
fn func<T>(t: &T) {}
Is actually this:
fn func<T: Sized>(t: &T) {}
Which may not always be what you want so it's the only trait you have to explicitly opt-out of like so:
fn func<T: ?Sized>(t: &T) {}
So in your case:
impl<T> From<T> for Arc<T> {
fn from(t: T) -> Arc<T>
}
Is actually:
impl<T: Sized> From<T> for Arc<T> {
fn from(t: T) -> Arc<T>
}
Which is why you can't some_value.into()
an Arc<dyn Anything>
since all trait objects are unsized.
As to why this restriction exists in the first place we can determine that by looking at the definition of From<T>
:
pub trait From<T> {
fn from(T) -> Self;
}
from(T)
means it has to take some T
and put it in the function's call stack, which means T
has to have a known size at compile-time and must therefore be Sized
.
Update
So this also applies to Arc::new(T)
since that function is defined in an impl block like so:
impl<T> for Arc<T> {
fn new(T) -> Arc<T> {
...
}
}
And when you call Arc::new(TraitImpl);
you are indeed calling it with a Sized
type since TraitImpl
's size is known at compile-time but then an unsized coercion is triggered by the let
variable binding since you ask Rust to treat Arc<TraitImpl>
as if it was an Arc<dyn Trait>
.
This unsized coercion isn't triggered when you call value.into()
since From<T>
only takes Sized
types.
However, if you are determined to use From<T>
you can do so like this:
use std::sync::Arc;
trait Trait {}
struct TraitImpl {}
impl Trait for TraitImpl {}
fn main() {
let value = TraitImpl {};
let _: Arc<dyn Trait> = Arc::new(value); // compiles
let value = TraitImpl {};
let _: Arc<dyn Trait> = <Arc<TraitImpl>>::from(value); // also compiles
}
In this example you make it clear you're going from a sized type to another sized type, i.e. TraitImpl
to Arc<TraitImpl>
, before you trigger the unsized coercion Arc<TraitImpl>
to Arc<dyn Trait>
.
Here are other variations:
use std::sync::Arc;
trait Trait {}
struct TraitImpl {}
impl Trait for TraitImpl {}
fn main() {
let value = TraitImpl {};
let _: Arc<dyn Trait> = Arc::new(value); // compiles
let value = TraitImpl {};
let _: Arc<dyn Trait> = <Arc<TraitImpl>>::from(value); // compiles
let value = TraitImpl {};
let _: Arc<dyn Trait> = Arc::from(value); // compiles, can infer Arc<TraitImpl> here
let value = TraitImpl {};
let _: Arc<dyn Trait> = Into::<Arc<TraitImpl>>::into(value); // compiles
let value = TraitImpl {};
let _: Arc<dyn Trait> = Into::<Arc<_>>::into(value); // compiles, can infer Arc<TraitImpl> here
let value = TraitImpl {};
let _: Arc<dyn Trait> = Into::into(value); // doesn't compile, infers Arc<dyn Trait> here
}
来源:https://stackoverflow.com/questions/61799081/why-cant-i-create-a-trait-object-with-let-arcdyn-trait-value-into