Confusing error message: the trait std::convert::Into cannot be made into an object
I've tried to make a function which will make a new thread, accepting either an &str or a String for the new thread's name:
/// Test helper to spawn a named thread.
///
/// The thread executes the work in the `f` closure.
#[cfg(test)]
fn named_thread<'s, F>(name: Into<String>, f: F) -> JoinHandle<()>
where F: FnOnce(), F: Send + 'static {
let thr = thread::Builder::new()
.name(String::from(name))
.spawn(f)
.unwrap();
thr
}
This gives:
error[E0038]: the trait `std::convert::Into` cannot be made into an object
--> src/lib.rs:377:1
|
377 | / fn named_thread<'s, F>(name: Into<String>, f: F) -> JoinHandle<()>
378 | | where F: FnOnce(), F: Send + 'static {
379 | | let name = name.into();
380 | | let thr = thread::Builder::new()
... |
384 | | thr
385 | | }
| |_^ the trait `std::convert::Into` cannot be made into an object
|
= note: the trait cannot require that `Self : Sized`
Which, from a user POV, is confusing on a few levels:
- I don't have any mention of
Sizedin my code (must be implicit). - Who is
Selfhere? - Even if I knew where
Sizedcame from, and whoSelfwas, the error message doesn't tell me what I did wrong.
I'd be interested to know what I did wrong, even if the error message can't be improved.
Thanks
This works for me:
fn named_thread<F, T>(name: T, f: F) -> JoinHandle<()> where F: FnOnce(), F: Send + 'static, T: Into<String> { thread::Builder::new() .name(name.into()) .spawn(f) .unwrap() }You cannot use
std::convert::Intoas if it was an object because it is just a trait. You have to use trait bounds for what you want to achieve. What the code above basically says is that T can be any type that implements the traitstd::convert::Into<String>which guarantees thatname.into()will return aString. All objects that you want to construct must have a known size at compile-time which the traitstd::marker::Sizedexpresses. Sincestd::convert::Intois not an object, and thus has no size, it doesn't implement the traitstd::marker::Sized. The error message expresses exactly what the problem is but I agree with you that it's confusing. It could be made clearer :)srccde at 2017-12-18 12:54:48
Thanks. After asking on IRC, I eventually ended up at the same code as you have above, and it works.
I think I understand after reading: https://doc.rust-lang.org/book/first-edition/trait-objects.html
In my own words, passing a trait directly (i.e. without a trait object involved):
- Uses static dispatch at the cost of code size.
- But will only work if the argument implements
Sized. Presumably, to make the static dispatch work, Rust needs to know the size of the incoming data for storing it, e.g., on the stack.
And, I think, if you use a
whereclause with trait constraints:- You are using a trait object, with dynamic dispatch via an implicit reference (and since references all have the same size, they are trivially
Sized). - There will be a runtime cost due to the use of a vtable.
Do I understand correctly?
Thank you for your comments.
EDIT: Fix trait objects discussion.
Edd Barrett at 2017-12-18 13:38:02
@vext01 I realize this comment of yours is nearly two years old, so you probably have a good handle on this by now, but just for anybody else who comes by to read this later:
No, you have it backwards. When passing a trait object directly (which in rust >= 1.27 is done using
dyn Traitnotation, making it more obvious), that's when you get dynamic dispatch. This is where the vtable and runtime cost comes in. You can only pass traits objects by reference, not by value though, so you'd need something like&dyn TraitorBox<dyn Trait>. But you cannot do this with a trait likeInto, becauseIntohas aSelf: Sizedconstraint, and traits that areSelf: Sizedare not object-safe. Only object-safe traits can be used as trait objects, and therefore with dynamic dispatch.When you do the
<T: Into>or<T> where T: Intoapproach is when you get static dispatch. What happens during compile is that Rust takes yourTtype and makes a copy of the function with that concrete type in place ofT, in a process called monomorphization.Alexander Krivács Schrøder at 2019-09-03 04:59:10
Triage: no change.
Esteban Kuber at 2020-02-01 20:58:31
Triage: Better span, should suggest using generics?
error[E0038]: the trait `Into` cannot be made into an object --> src/lib.rs:3:30 | 3 | fn named_thread<'s, F>(name: Into<String>, f: F) -> JoinHandle<()> | ^^^^^^^^^^^^ `Into` cannot be made into an object | = note: the trait cannot be made into an object because it requires `Self: Sized` = note: for a trait to be "object safe" it needs to allow building a vtable to allow the call to be resolvable dynamically; for more information visit <https://doc.rust-lang.org/reference/items/traits.html#object-safety>Esteban Kuber at 2023-02-03 15:51:54
Current output:
error[E0038]: the trait `Into` cannot be made into an object --> src/lib.rs:3:30 | 3 | fn named_thread<'s, F>(name: dyn Into<String>, f: F) -> JoinHandle<()> | ^^^^^^^^^^^^^^^^ `Into` cannot be made into an object | = note: the trait cannot be made into an object because it requires `Self: Sized` = note: for a trait to be "object safe" it needs to allow building a vtable to allow the call to be resolvable dynamically; for more information visit <https://doc.rust-lang.org/reference/items/traits.html#object-safety> error[E0277]: the size for values of type `(dyn Into<String> + 'static)` cannot be known at compilation time --> src/lib.rs:3:24 | 3 | fn named_thread<'s, F>(name: dyn Into<String>, f: F) -> JoinHandle<()> | ^^^^ doesn't have a size known at compile-time | = help: the trait `Sized` is not implemented for `(dyn Into<String> + 'static)` = help: unsized fn params are gated as an unstable feature help: you can use `impl Trait` as the argument type | 3 | fn named_thread<'s, F>(name: impl Into<String>, f: F) -> JoinHandle<()> | ~~~~ help: function arguments must have a statically known size, borrowed types always have a known size | 3 | fn named_thread<'s, F>(name: &dyn Into<String>, f: F) -> JoinHandle<()> | + error[E0277]: the trait bound `String: From<dyn Into<String>>` is not satisfied --> src/lib.rs:6:37 | 6 | ... .name(String::from(name)) | ^^^^^^ the trait `From<dyn Into<String>>` is not implemented for `String` | = help: the following other types implement trait `From<T>`: <String as From<char>> <String as From<Box<str>>> <String as From<Cow<'a, str>>> <String as From<&str>> <String as From<&mut str>> <String as From<&String>> error[E0038]: the trait `Into` cannot be made into an object --> src/lib.rs:6:37 | 6 | ... .name(String::from(name)) | ^^^^^^^^^^^^ `Into` cannot be made into an object | = note: the trait cannot be made into an object because it requires `Self: Sized` = note: for a trait to be "object safe" it needs to allow building a vtable to allow the call to be resolvable dynamically; for more information visit <https://doc.rust-lang.org/reference/items/traits.html#object-safety> error[E0277]: the size for values of type `dyn Into<String>` cannot be known at compilation time --> src/lib.rs:6:50 | 6 | ... .name(String::from(name)) | ------------ ^^^^ doesn't have a size known at compile-time | | | required by a bound introduced by this call | = help: the trait `Sized` is not implemented for `dyn Into<String>` note: required by a bound in `from` --> /playground/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/core/src/convert/mod.rs:579:16 | 579 | pub trait From<T>: Sized { | ^ required by this bound in `From::from` ... 584 | fn from(value: T) -> Self; | ---- required by a bound in this associated functionThe E0038 should suggest
implinstead ofdyn, like E0277 does. The additional object safety errors onString::fromare redundant. The desired end code is https://play.rust-lang.org/?version=nightly&mode=debug&edition=2018&gist=93a49bf8ddfb295dc579f4aed6bd5d2cEsteban Kuber at 2024-02-20 18:11:21