I'm having trouble understanding how values of boxed traits come into existence. Consider the following code:
trait Fooer {
fn foo(&self);
}
impl Fooer for i32 {
fn foo(&self) { println!("Fooer on i32!"); }
}
fn main() {
let a = Box::new(32); // works, creates a Box<i32>
let b = Box::<i32>::new(32); // works, creates a Box<i32>
let c = Box::<dyn Fooer>::new(32); // doesn't work
let d: Box<dyn Fooer> = Box::new(32); // works, creates a Box<Fooer>
let e: Box<dyn Fooer> = Box::<i32>::new(32); // works, creates a Box<Fooer>
}
Obviously, variant a and b work, trivially. However, variant c does not, probably because the new function takes only values of the same type which is not the case since Fooer != i32. Variant d and e work, which lets me suspect that some kind of automatic conversion from Box<i32> to Box<dyn Fooer> is being performed.
So my questions are:
Does some kind of conversion happen here?
If so, what the mechanism behind it and how does it work? (I'm also interested in the low level details, i.e. how stuff is represented under the hood)
Is there a way to create a Box<dyn Fooer> directly from an i32? If not: why not?
However, variant c does not, probably because the new function takes only values of the same type which is not the case since Fooer != i32.
No, it's because there is no new function for Box<dyn Fooer>. In the documentation:
impl<T> Box<T>
pub fn new(x: T) -> Box<T>
Most methods on Box<T> allow T: ?Sized, but new is defined in an impl without a T: ?Sized bound. That means you can only call Box::<T>::new when T is a type with a known size. dyn Fooer is unsized, so there simply isn't a new function to call.
In fact, that function can't exist in today's Rust. Box<T>::new needs to know the concrete type T so that it can allocate memory of the right size and alignment. Therefore, you can't erase T before you send it to Box::new. (It's conceivable that future language extensions may allow functions to accept unsized parameters; however, it's unclear whether even unsized_locals would actually enable Box<T>::new to accept unsized T.)
For the time being, unsized types like dyn Fooer can only exist behind a "fat pointer", that is, a pointer to the object and a pointer to the implementation of Fooer for that object. How do you get a fat pointer? You start with a thin pointer and coerce it. That's what's happening in these two lines:
let d: Box<Fooer> = Box::new(32); // works, creates a Box<Fooer>
let e: Box<Fooer> = Box::<i32>::new(32); // works, creates a Box<Fooer>
Box::new returns a Box<i32>, which is then coerced to Box<Fooer>. You could consider this a conversion, but the Box isn't changed; all the compiler does is stick an extra pointer on it and forget its original type. rodrigo's answer goes into more detail about the language-level mechanics of this coercion.
Hopefully all of this goes to explain why the answer to
Is there a way to create a Box<Fooer> directly from an i32?
is "no": the i32 has to be boxed before you can erase its type. It's the same reason you can't write let x: Fooer = 10i32.
Related
Why can't I write a function with the same type as Box::new?
Are polymorphic variables allowed?
How do you actually use dynamically sized types in Rust?
Why is `let ref a: Trait = Struct` forbidden?
I'll try to explain what conversions (coercions) happen in your code.
There is a marker trait named Unsize that, between others:
Unsize is implemented for:
T is Unsize<Trait> when T: Trait.
[...]
This trait, AFAIK, is not used directly for coercions. Instead, CoerceUnsized is used. This trait is implemented in a lot of cases, some of them are quite expected, such as:
impl<'a, 'b, T, U> CoerceUnsized<&'a U> for &'b T
where
'b: 'a,
T: Unsize<U> + ?Sized,
U: ?Sized
that is used to coerce &i32 into &Fooer.
The interesting, not so obvious implementation for this trait, that affects your code is:
impl<T, U> CoerceUnsized<Box<U>> for Box<T>
where
T: Unsize<U> + ?Sized,
U: ?Sized
This, together with the definition of the Unsize marker, can be somewhat read as: if U is a trait and T implements U, then Box<T> can be coerced into Box<U>.
About your last question:
Is there a way to create a Box<Fooer> directly from an i32? If not: why not?
Not that I know of. The problem is that Box::new(T) requires a sized value, since the value passed is moved into the box, and unsized values cannot be moved.
In my opinion, the easiest way to do that is to simply write:
let c = Box::new(42) as Box<Fooer>;
That is, you create a Box of the proper type and then coerce to the unsized one (note it looks quite similar to your d example).
Related
I'm having trouble understanding how values of boxed traits come into existence. Consider the following code:
trait Fooer {
fn foo(&self);
}
impl Fooer for i32 {
fn foo(&self) { println!("Fooer on i32!"); }
}
fn main() {
let a = Box::new(32); // works, creates a Box<i32>
let b = Box::<i32>::new(32); // works, creates a Box<i32>
let c = Box::<dyn Fooer>::new(32); // doesn't work
let d: Box<dyn Fooer> = Box::new(32); // works, creates a Box<Fooer>
let e: Box<dyn Fooer> = Box::<i32>::new(32); // works, creates a Box<Fooer>
}
Obviously, variant a and b work, trivially. However, variant c does not, probably because the new function takes only values of the same type which is not the case since Fooer != i32. Variant d and e work, which lets me suspect that some kind of automatic conversion from Box<i32> to Box<dyn Fooer> is being performed.
So my questions are:
Does some kind of conversion happen here?
If so, what the mechanism behind it and how does it work? (I'm also interested in the low level details, i.e. how stuff is represented under the hood)
Is there a way to create a Box<dyn Fooer> directly from an i32? If not: why not?
However, variant c does not, probably because the new function takes only values of the same type which is not the case since Fooer != i32.
No, it's because there is no new function for Box<dyn Fooer>. In the documentation:
impl<T> Box<T>
pub fn new(x: T) -> Box<T>
Most methods on Box<T> allow T: ?Sized, but new is defined in an impl without a T: ?Sized bound. That means you can only call Box::<T>::new when T is a type with a known size. dyn Fooer is unsized, so there simply isn't a new function to call.
In fact, that function can't exist in today's Rust. Box<T>::new needs to know the concrete type T so that it can allocate memory of the right size and alignment. Therefore, you can't erase T before you send it to Box::new. (It's conceivable that future language extensions may allow functions to accept unsized parameters; however, it's unclear whether even unsized_locals would actually enable Box<T>::new to accept unsized T.)
For the time being, unsized types like dyn Fooer can only exist behind a "fat pointer", that is, a pointer to the object and a pointer to the implementation of Fooer for that object. How do you get a fat pointer? You start with a thin pointer and coerce it. That's what's happening in these two lines:
let d: Box<Fooer> = Box::new(32); // works, creates a Box<Fooer>
let e: Box<Fooer> = Box::<i32>::new(32); // works, creates a Box<Fooer>
Box::new returns a Box<i32>, which is then coerced to Box<Fooer>. You could consider this a conversion, but the Box isn't changed; all the compiler does is stick an extra pointer on it and forget its original type. rodrigo's answer goes into more detail about the language-level mechanics of this coercion.
Hopefully all of this goes to explain why the answer to
Is there a way to create a Box<Fooer> directly from an i32?
is "no": the i32 has to be boxed before you can erase its type. It's the same reason you can't write let x: Fooer = 10i32.
Related
Why can't I write a function with the same type as Box::new?
Are polymorphic variables allowed?
How do you actually use dynamically sized types in Rust?
Why is `let ref a: Trait = Struct` forbidden?
I'll try to explain what conversions (coercions) happen in your code.
There is a marker trait named Unsize that, between others:
Unsize is implemented for:
T is Unsize<Trait> when T: Trait.
[...]
This trait, AFAIK, is not used directly for coercions. Instead, CoerceUnsized is used. This trait is implemented in a lot of cases, some of them are quite expected, such as:
impl<'a, 'b, T, U> CoerceUnsized<&'a U> for &'b T
where
'b: 'a,
T: Unsize<U> + ?Sized,
U: ?Sized
that is used to coerce &i32 into &Fooer.
The interesting, not so obvious implementation for this trait, that affects your code is:
impl<T, U> CoerceUnsized<Box<U>> for Box<T>
where
T: Unsize<U> + ?Sized,
U: ?Sized
This, together with the definition of the Unsize marker, can be somewhat read as: if U is a trait and T implements U, then Box<T> can be coerced into Box<U>.
About your last question:
Is there a way to create a Box<Fooer> directly from an i32? If not: why not?
Not that I know of. The problem is that Box::new(T) requires a sized value, since the value passed is moved into the box, and unsized values cannot be moved.
In my opinion, the easiest way to do that is to simply write:
let c = Box::new(42) as Box<Fooer>;
That is, you create a Box of the proper type and then coerce to the unsized one (note it looks quite similar to your d example).
I'm running into an issue with trait bounds and can't understand what I'm doing wrong. I'm working with the arduino-uno crate from avr-hal and I have a function that reads the ADC, implemented as follows:
fn read_signal<T: avr_hal_generic::hal::adc::Channel<board::adc::Adc, ID = u8>>(
adc: &mut board::adc::Adc,
pinA0: &mut T,
) {
let x = adc.read(&mut pinA0);
}
However, I receive the following error:
the trait bound `&mut T: avr_hal_generic::embedded_hal::adc::Channel<arduino_uno::adc::Adc>` is not satisfied
required because of the requirements on the impl of `arduino_uno::prelude::_embedded_hal_adc_OneShot<arduino_uno::adc::Adc, _, &mut T>` for `arduino_uno::adc::Adc` rustc(E0277)
I have tried using where instead, but that doesn't help. How can I fix this?
Also, though I show this specific example, I would really appreciate an explanation or pointers to better documentation on this subject/error as I've encountered it a few times and struggle to understand what is wrong.
The compiler error says:
the trait bound &mut T: avr_hal_generic::embedded_hal::adc::Channel<arduino_uno::adc::Adc> is not satisfied
Notice that the error is asking for &mut T to implement Channel, &mut T: Channel<...>, whereas your T has the bound T: Channel<...> — applying to T itself rather than &mut T. That's why the bound you've already written isn't helping.
Now, what's the right fix? If I look at the docs you linked, I can find the type of <Adc as OneShot>::read. (Note: I copied the text from the docs to construct this snippet; I didn't read the source code.)
impl<WORD, PIN> OneShot<Adc, WORD, PIN> for Adc
where
WORD: From<u16>,
PIN: Channel<Adc, ID = MUX_A>, // <---- look here
{
pub fn read(
&mut self,
_pin: &mut PIN // <---- look here
) -> Result<WORD, ...>
}
So, read should be given an &mut PIN and the type variable PIN should implement Channel. That all sounds reasonable, but in your code the compiler thinks we want &mut T to implement Channel. An extra &mut has appeared. Where did it come from? You wrote:
fn read_signal<T: avr_hal_generic::hal::adc::Channel<board::adc::Adc, ID = u8>>(
adc: &mut board::adc::Adc,
pinA0: &mut T,
) {
let x = adc.read(&mut pinA0);
}
pinA0 is of type &mut T, but you then wrote adc.read(&mut pinA0), which means the parameter to read() is of type &mut &mut T. Since read wants &mut of something implementing Channel, this results in the error you saw, asking for &mut T: Channel.
The fix, then, is to not take &mut of &mut:
let x = adc.read(pinA0);
Under some circumstances, Rust provides implicit coercions which will, in particular, turn a reference to a reference (or a reference to a 'smart pointer' type implementing Deref) into a reference. This is why you might have had extraneous & or &mut work in the past. However, these coercions are not applied when there isn't a single concrete type that's expected, such as in this case where the function parameter's type contains the type variable T.
I have a variable tokens: &[AsRef<str>] and I want to concatenate it into single string:
// tokens: &[AsRef<str>]
let text = tokens.join("") // Error
let text = tokens.iter().map(|x|x.as_ref()).collect::<Vec<_>>().join("") // Ok, but...
The second is awkward and inefficient because it reallocates items to a new Vec.
According to the source code, join can be applied to tokens if its type is &[Borrow<str>]:
// if tokens: &[Borrow<str>]
let text = tokens.join("") // OK
// so I want to convert &[AsRef<str>] to &[Borrow<str>]
let text = convert_to_borrow(tokens).join("")
How should I do that? Why is Join implemented for types that implement Borrow but not AsRef?
It may be slightly slower, but you can collect an iterator of &strs directly into a String.
let text: String = tokens.iter().map(|s| s.as_ref()).collect();
This is possible because String implements FromIterator<&'_ str>. This method grows the String by repeatedly calling push_str, which may mean it has to be reallocated a number of times, but it does not create an intermediate Vec<&str>. Depending on the size of the slices and strings being used this may be slower (although in some cases it could also be slightly faster). If the difference would be significant to you, you should benchmark both versions.
There is no way to treat a slice of T: AsRef<str> as if it were a slice of T: Borrow<str> because not everything that implements AsRef implements Borrow, so in generic code the compiler can't know what Borrow implementation to apply.
Trentcl's answer gives you a solution to your actual problem by relying only on the AsRef<str> implementation. What follows is more of an answer to the more general question in your title.
Certain traits carry with them invariants which implementations must enforce. In particular, if implementations of Borrow<T> also implement Eq, Hash, and Ord then the implementations of those traits for T must behave identically. This requirement is a way of saying that the borrowed value is "the same" as the original value, but just viewed in a different way. For example the String: Borrow<str> implementation must return the entire string slice; it would be incorrect to return a subslice.
AsRef does not have this restriction. An implementation of AsRef<T> can implement traits like Hash and Eq in a completely different way from T. If you need to return a reference to just a part of a struct, then AsRef can do it, while Borrow cannot.
All this means that you cannot derive a valid Borrow<T> implementation from an arbitrary AsRef<T> implementation: the AsRef implementation may not enforce the invariants that Borrow requires.
However, the other way around does work. You can create an implementation of AsRef<T>, given an arbitrary Borrow<T>:
use std::borrow::Borrow;
use std::convert::AsRef;
use std::marker::PhantomData;
pub struct BorrowAsRef<'a, T: ?Sized, U: ?Sized>(&'a T, PhantomData<U>);
impl<'a, T, U> AsRef<U> for BorrowAsRef<'a, T, U>
where
T: Borrow<U> + ?Sized,
U: ?Sized,
{
fn as_ref(&self) -> &U {
self.0.borrow()
}
}
pub trait ToAsRef<T: ?Sized, U: ?Sized> {
fn to_as_ref(&self) -> BorrowAsRef<'_, T, U>;
}
impl<T, U> ToAsRef<T, U> for T
where
T: ?Sized + Borrow<U>,
U: ?Sized,
{
fn to_as_ref(&self) -> BorrowAsRef<'_, T, U> {
BorrowAsRef(self, PhantomData)
}
}
fn borrowed(v: &impl Borrow<str>) {
needs_as_ref(&v.to_as_ref())
}
fn needs_as_ref(v: &impl AsRef<str>) {
println!("as_ref: {:?}", v.as_ref())
}
Why is Join implemented for types that implement Borrow but not AsRef?
This is a blanket implementation, for all types that implement Borrow<str>, which means it can't also be implemented for types that implement AsRef<str>. Even with the unstable feature min_specialization enabled, it wouldn't work because having an implementation of AsRef is not more "specific" than having an implementation of Borrow. So they had to pick one or the other.
It could be argued that AsRef would have been a better choice because it covers more types. But unfortunately I don't think this can be changed now because it would be a breaking change.
I want a struct's field to be an Iterator over T elements, however Iterator doesn't have a type parameter. I know I'm trying to do something fundamentally wrong, what though?
In Rust, Iterator (more specifically Iterator<Item = T> where T is the type yielded when iterating) is a trait. Types that represent iterators implement this trait.
This is an instance of the more general question "how can I store a value of a trait in a struct?". I deliberately phrased the question in an imprecise way -- what we actually want is to store a value of a type implementing the trait, and there are broadly two ways to do that.
Put it in a box
The easiest way is to use a Box<Iterator<Item = T>>. The Box represents an owned pointer, and when you have a pointer to a trait object, Rust creates a "fat pointer" including runtime information about the type. This way the concrete type of the iterator is not known at compile time.
The definition looks like this:
struct IteratorHolder {
iter: Box<Iterator<Item = u32>>,
}
And usage:
let ih = IteratorHolder { iter: Box::new(0..10) };
println!("{:?}", ih.iter.collect::<Vec<_>>());
Generics
Another way to do the same thing, while avoiding allocating any boxes on the heap, is to use a generic struct. This way we will have some type implementing Iterator<Item = T>, and what it is concretely will be determined at compile time.
Definition:
struct IteratorHolder<I: Iterator<Item = u32>> {
iter: I,
}
And usage:
let ih = IteratorHolder { iter: 0..10 };
println!("{:?}", ih.iter.collect::<Vec<_>>());
I have the following code
extern crate rand;
use rand::Rng;
pub struct Randomizer {
rand: Box<Rng>,
}
impl Randomizer {
fn new() -> Self {
let mut r = Box::new(rand::thread_rng()); // works
let mut cr = Randomizer { rand: r };
cr
}
fn with_rng(rng: &Rng) -> Self {
let mut r = Box::new(*rng); // doesn't work
let mut cr = Randomizer { rand: r };
cr
}
}
fn main() {}
It complains that
error[E0277]: the trait bound `rand::Rng: std::marker::Sized` is not satisfied
--> src/main.rs:16:21
|
16 | let mut r = Box::new(*rng);
| ^^^^^^^^ `rand::Rng` does not have a constant size known at compile-time
|
= help: the trait `std::marker::Sized` is not implemented for `rand::Rng`
= note: required by `<std::boxed::Box<T>>::new`
I don't understand why it requires Sized on Rng when Box<T> doesn't impose this on T.
More about the Sized trait and bound - it's a rather special trait, which is implicitly added to every function, which is why you don't see it listed in the prototype for Box::new:
fn new(x: T) -> Box<T>
Notice that it takes x by value (or move), so you need to know how big it is to even call the function.
In contrast, the Box type itself does not require Sized; it uses the (again special) trait bound ?Sized, which means "opt out of the default Sized bound":
pub struct Box<T> where T: ?Sized(_);
If you look through, there is one way to create a Box with an unsized type:
impl<T> Box<T> where T: ?Sized
....
unsafe fn from_raw(raw: *mut T) -> Box<T>
so from unsafe code, you can create one from a raw pointer. From then on, all the normal things work.
The problem is actually quite simple: you have a trait object, and the only two things you know about this trait object are:
its list of available methods
the pointer to its data
When you request to move this object to a different memory location (here on the heap), you are missing one crucial piece of information: its size.
How are you going to know how much memory should be reserved? How many bits to move?
When an object is Sized, this information is known at compile-time, so the compiler "injects" it for you. In the case of a trait-object, however, this information is unknown (unfortunately), and therefore this is not possible.
It would be quite useful to make this information available and to have a polymorphic move/clone available, but this does not exist yet and I do not remember any proposal for it so far and I have no idea what the cost would be (in terms of maintenance, runtime penalty, ...).
I also want to post the answer, that one way to deal with this situation is
fn with_rng<TRand: Rng>(rng: &TRand) -> Self {
let r = Box::new(*rng);
Randomizer { rand: r }
}
Rust's monomorphism will create the necessary implementation of with_rng replacing TRand by a concrete sized type. In addition, you may add a trait bound requiring TRand to be Sized.