65091

Why can't I create a trait object with let _: Arc = value.into()?

<h3>Question</h3>
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> }
<h3>Answer1:</h3>

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.

<hr />

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.

<hr />

The <em>TL;DR;</em> 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.


<h3>Answer2:</h3>

For all generic Rust code there is an implicit Sized bound on any T. This:

<pre class="lang-rust prettyprint-override">fn func<T>(t: &T) {}

Is actually this:

<pre class="lang-rust prettyprint-override">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:

<pre class="lang-rust prettyprint-override">fn func<T: ?Sized>(t: &T) {}

So in your case:

<pre class="lang-rust prettyprint-override">impl<T> From<T> for Arc<T> { fn from(t: T) -> Arc<T> }

Is actually:

<pre class="lang-rust prettyprint-override">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>:

<pre class="lang-rust prettyprint-override">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 <em>an unsized coercion</em> 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:

<pre class="lang-rust prettyprint-override">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:

<pre class="lang-rust prettyprint-override">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

Recommend

  • SwiftUI: Cancel TapGesture on parent view
  • Matlab: Subplot for 2 rows and 10 columns
  • Amazon MWS signature issue
  • Installing older version of R
  • Why stored procedure return -1
  • When reading a binary file with java, how? [closed]
  • Resteasy Server-side Mock Framework
  • How to place UI widgets on top of multiple Z ordered Surface Views in Android
  • Buildbot running sequential builders after they're finished
  • JavaFX TabPane System like in a browser
  • iterating through image folder using javascript and adding the result in HTML
  • cordova edit-config not updating AndroidManifest.xml
  • Velocity (VM) template request parameters: Getting GET variables
  • Using Autofac with AJAX-enabled WCF Service
  • Specify the _id field using Bulk.IndexMany in ElasticSearch
  • Magento Layered Navigation block. Move to center
  • OpenCV::solvePNP() - Assertion failed
  • How to make a dependent dropdown in codeigniter
  • Custom Data Generator for Keras LSTM with TimeSeriesGenerator
  • Inet6Address valid for invalid IPv6 Address
  • Get last 15 lines from a large file in SFTP with phpseclib [duplicate]
  • Compiling and linking NASM and 64-bit C code together into a bootloader [duplicate]
  • MFMailComposer send email without presenting view
  • How to modify the way a ForeignKey field is rendered in a Django admin page to avoid browser crash?
  • Facebook friend list in Facebook Android SDK 3.14
  • C++11: Why rvalue reference parameter implicitly converted to lvalue
  • Debugging php script timeout?
  • how to run a different select statement based on condition in Hive SQL
  • Creating 2d platforms using JavaScript
  • How to restrict use of third party camera app from your app
  • how do i compare two rows and store the similarities of the two rows in another column
  • How to use Streams api peek() function and make it work?
  • Change cell value based on cell color in google spreadsheet
  • how to snap two objects in runtime in unity?
  • Another “Cannot make static reference…” Question
  • How to decleare char *const argv[] in swift [duplicate]
  • How to get rgb from transparent pixel in js
  • WPF custom control and direct content support
  • Create/delete users from text file using Bash script