Trying to implement the Debug trait for a custom type I stumbled upon the implementation for Vec<T>. I have difficulties understanding how it works.
The implementation goes like this:
impl<T: fmt::Debug> fmt::Debug for Vec<T> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
fmt::Debug::fmt(&**self, f)
}
}
I understand it calls the fmt implementation for some other type. What I cannot understand is what type it is. I've tried to figure it out with the help of another question, and searching among the implementations of Debug for something that looks appropriate (maybe something like &[T]), but with no success.
What is the exact meaning of &**self in this context? What implementation of Debug is being called?
In cases like this, I find it useful to make the compiler tell you what the type is. Just cause a type error and let the compiler diagnostics do it for you. The easiest way is to try to assign your item to something of type ():
fn main() {
let v = &vec![1,2,3];
let () = v;
let () = &**v;
}
The errors are:
<anon>:3:9: 3:11 error: mismatched types:
expected `&collections::vec::Vec<_>`,
found `()`
(expected &-ptr,
found ()) [E0308]
<anon>:3 let () = v;
^~
<anon>:4:9: 4:11 error: mismatched types:
expected `&[_]`,
found `()`
(expected &-ptr,
found ()) [E0308]
<anon>:4 let () = &**v;
^~
Thus v is a &collections::vec::Vec<_> and &**v is a &[_].
More detailed, Vec has this:
impl<T> Deref for Vec<T> {
type Target = [T];
// ...
}
So, we dereference once to go from &Vec<T> to a Vec<T>, dereference again to get a [T], and then reference once to get a &[T].
[T] has this:
impl<T> Debug for [T] {
fn fmt(&self, ...) ...;
}
However, when searching for an appropriate method to call, Rust will automatically attempt to dereference the target. That means we can find the method on [T] from a &[T].
As corrected by Francis Gagné, Debug::fmt takes &self, so directly calling it with a &[T] finds the matching implementation. No need for any automatic referencing or dereferencing.
Related
I am trying to implement a trait for a struct which in turn has functions that return traits.
I want this, because I do not want to bind the uer to a specific data structure.
However, trying to apply the compiler's correction suggestions, I fell deeper and deeper into a rabbit hole to no avail.
Here's a minimal example of what I'm trying to do:
trait WordsFilter {
fn starting_with(&self, chr: char) -> dyn Iterator<Item = String>;
}
struct WordsContainer {
words: Vec<String>,
}
impl WordsFilter for WordsContainer {
fn starting_with(&self, chr: char) -> dyn Iterator<Item = String>
{
self.words.iter().filter(|word| word.starts_with("a"))
}
}
fn main() {}
Which results in:
error[E0277]: the size for values of type `(dyn Iterator<Item = String> + 'static)` cannot be known at compilation time
--> .\traits.rs:10:40
|
10 | fn starting_with(&self, chr: char) -> dyn Iterator<Item = String>
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^ doesn't have a size known at compile-time
|
= help: the trait `Sized` is not implemented for `(dyn Iterator<Item = String> + 'static)`
= note: the return type of a function must have a statically known size
error: aborting due to previous error
For more information about this error, try `rustc --explain E0277`.
I tried to apply the compiler's correction's step by step but they were just getting more.
TL;DR Use generics or associated types.
You cannot use bare dyn Trait types. They are Unsized which means that compiler cannot know their size at the compile time. Therefore you can only use them behind some reference (similarly to how you cannot use str or [T] and must use &str and &[T]). The easiest way is to use Box<dyn Trait>, like PitaJ earlier suggested. This requires of course one heap allocation, but allows a very nice API.
In normal functions and methods you can however use impl Trait trick to make compiler infer returned type. This is used like this
fn foo() -> impl Iterator<Item = ()> {
todo!()
}
Calling this function would return some concrete object that implements given trait. This however is currently not possible in Trait definitions. There is active development and hopefully in the future we will be able to use it. However now it's not the case. So following code will not compile!
// XXX: This doesn't currently work!
trait Foo {
fn foo() -> impl Iterator<Item = ()>;
}
There is luckily a simple solution that wouldn't require boxing trait objects. You can define an associated type and require each implementator to specify a concrete type. You can also require that this type must implement the Iterator trait.
trait WordsFilter {
type Iter: Iterator<Item = String>;
fn starting_with(&self, chr: char) -> Self::Iter;
}
This would make each implementation specify one concrete type of returned iterator. If you would like to be able to return multiple different iterators for a single type you can use generics.
trait WordsFilter<I>
where
I: Iterator<Item = String>
{
fn starting_with(&self, chr: char) -> I;
}
There are several things that need to be fixed to get this to work.
I'll first show the workng version then step through what had to change and why.
trait WordsFilter {
fn starting_with(&self, chr: char) -> Box<dyn Iterator<Item = String> + '_>;
}
struct WordsContainer {
words: Vec<String>,
}
impl WordsFilter for WordsContainer {
fn starting_with(&self, chr: char) -> Box<dyn Iterator<Item = String> + '_>
{
Box::new(self.words.iter().filter(|word| word.starts_with("a")).cloned())
}
}
fn main() {}
Boxing the iterator. Rust doesn't allow you to return naked dyn types. The compiler error tells you to Box it. So I did.
Clone after filter. Vec<X>::iter() returns an iterator with Item=&X so in this example we get Item=&String we want Item=String so we need to clone.
Add a lifetime. The problem was that without the lifetime checks I could write this...
let words_container = WordsContainer{...};
let it = words_container.starting_with();
drop(words_container)
it.next()
but it.next() is stepping through the internal vector that is inside words_container - which no longer exists.
Adding the '_ lifetime on the trait is saying the iterator is only valid for as long as the underlying container is. So now the above code would generate a compile error.
Motivation
I want to read a stream of values for multiple files on disc. These might be CSV files, or tab-separated, or some proprietary binary format. Therefore I want my function that handles reading multiple files to take the Path -> Iterator<Data> function as an argument. If I understand correctly, in Rust I need to box the iterator, and the function itself, since they're unsized. Therefore my reading function should be (I'm just using i32 as a simple proxy for my data here):
fn foo(read_from_file: Box<dyn Fn(&Path) -> Box<dyn Iterator<Item=i32>>>) {
panic!("Not implemented");
}
For testing, I'd rather not be reading actual files from disc. I'd like my test data to be right there in the test module. Here's roughly what I want, but I've just put it into the main of a bin project for simplicity:
use std::path::Path;
fn foo(read_from_file: Box<dyn Fn(&Path) -> Box<dyn Iterator<Item=i32>>>) {
panic!("Not implemented");
}
fn main() {
let read_from_file = Box::new(|path: &Path| Box::new(match path.as_os_str().to_str().unwrap() {
"/my_files/data.csv" => vec![1, 2, 3],
"/my_files/data_2.csv" => vec![4, 5, 6],
_ => panic!("Invalid filename"),
}.into_iter()));
foo(read_from_file);
}
The error
This gives me a compilation error:
Compiling iter v0.1.0 (/home/harry/coding/rust_sandbox/iter)
error[E0271]: type mismatch resolving `for<'r> <[closure#src/main.rs:9:35: 13:19] as FnOnce<(&'r Path,)>>::Output == Box<(dyn Iterator<Item = i32> + 'static)>`
--> src/main.rs:15:9
|
15 | foo(read_from_file);
| ^^^^^^^^^^^^^^ expected trait object `dyn Iterator`, found struct `std::vec::IntoIter`
|
= note: expected struct `Box<(dyn Iterator<Item = i32> + 'static)>`
found struct `Box<std::vec::IntoIter<{integer}>>`
= note: required for the cast to the object type `dyn for<'r> Fn(&'r Path) -> Box<(dyn Iterator<Item = i32> + 'static)>`
For more information about this error, try `rustc --explain E0271`.
error: could not compile `iter` due to previous error
I don't really understand this. Doesn't std::vec::IntoIter implement Iterator, in which case I don't see why this is a type error?
The fix, which I also don't understand
If I add an explicit type annotation Box<dyn Fn(&Path) -> Box<dyn Iterator<Item=i32>>>, this compiles:
use std::path::Path;
fn foo(read_from_file: Box<dyn Fn(&Path) -> Box<dyn Iterator<Item=i32>>>) {
panic!("Not implemented");
}
fn main() {
let read_from_file : Box<dyn Fn(&Path) -> Box<dyn Iterator<Item=i32>>>
= Box::new(|path: &Path| Box::new(match path.as_os_str().to_str().unwrap() {
"/my_files/data.csv" => vec![1, 2, 3],
"/my_files/data_2.csv" => vec![4, 5, 6],
_ => panic!("Invalid filename"),
}.into_iter()));
foo(read_from_file);
I'm very confused by why this works. My understanding of Rust is that, in a let definition, the explicit type is optional - unless the compiler cannot infer it, in which case the compiler should emit error[E0283]: type annotations required.
Pointers to dynamically sized types (DSTs) like Box<dyn Iterator<Item=i32>> are "fat". A Box<std::vec::IntoIter<i32>> is not a pointer to a DST (as the size of IntoIter is known), and hence can be a "thin" pointer simply pointing to the instance of IntoIter on the heap.
The creation and usage of a fat pointer is more expensive than that of a thin pointer. This is why, as #Aplet123 mentioned, you need to explicitly tell the compiler somehow (via type annotations or an as cast) that you want to cast the thin Box<std::vec::IntoIter<i32>> pointer generated by your closure to a fat Box<dyn Iterator<Item=i32>> pointer.
Note that if you remove the let binding and create the closure in the argument list of the foo function call, then the compiler makes the closure must return a fat pointer because of the argument type expected by foo.
To me, this reads like a failure of type inference, since the closure is unable to infer that it needs to return a pointer to a v-table (from dyn Iterator).
However, I'd suggest that Box<dyn Foo> might not be necessary here. It's true that, since Iterator is a trait, you can't know the size of it at compile-time, in a sense, you can.
Rust "monomorphizes" generic code, which means it generates copies of generic functions/structs/etc for each concrete type it is used with. For example, if you have:
struct Foo<T> {
value: T
}
fn main() {
let _ = Foo { value: "hello" };
let _ = Foo { value: 123 };
}
It's going to generate a Foo_str_'static and a Foo_i32 (roughly speaking) and substitute those in as needed.
You can exploit this to use static dispatch with generics while using traits. Your function can be rewritten as:
fn foo<F, I>(read_from_file: F)
where
F: Fn(&Path) -> I,
I: Iterator<Item = i32>,
{
unimplemented!()
}
fn main() {
// note the lack of boxing
let read_from_file = |path: &Path| {
// ...
};
foo(read_from_file);
}
This code is (very probably but I haven't benchmarked) faster, and more idiomatic, as well as making the compiler error go away.
Given a struct S implementing a trait T, why doesn't Box<S> implement Borrow<dyn T>?
The following code, that I would have expected to compile, doesn't:
trait T{}
struct S{}
impl T for S{}
fn f1(s: &S) -> &dyn T {
s
}
fn f2(s: &Box<S>) -> &dyn T {
std::borrow::Borrow::borrow(s)
}
Why does f1 compile while f2 doesn't? (The conversion from &S to &dyn T is done in the first case and not in the second).
This is to do with the way that type inference and type coercion work. The Borrow<B> trait's parameter is the type of the borrowed value, and the type checker needs to know what it is.
If you just write:
std::borrow::Borrow::borrow(s)
Then the type B in Borrow<B> will be inferred from the surrounding code. In your case it is inferred to be dyn T because that's the return value. However, dyn T is a completely different type from S, so it doesn't type-check.
Once the type checker knows that the value being returned is of type &S then it can coerce it to a &dyn T, but you need to give it that information:
fn f2(s: &Box<S>) -> &dyn T {
let s: &S = std::borrow::Borrow::borrow(s);
s
}
Or, more concisely:
fn f2(s: &Box<S>) -> &dyn T {
std::borrow::Borrow::<S>::borrow(s)
}
The reason why Sébastien Renauld's answer works is because Deref uses an associated type instead of a type parameter. The type-checker can easily infer the <S as Deref>::Target because there can only be one implementation of Deref per type and the associated Target type is uniquely determined. Borrow is different because Box<S> could implement Borrow<()>, Borrow<i32>, Borrow<Box<Option<Vec<bool>>>>,... so you have to be more explicit about which implementation you intend.
&Box<S> is not directly equal to Box<&S>, and this is why it does not compile directly.
You can relatively easily fix this by dereferencing, like so:
use std::ops::Deref;
trait T{}
struct S{}
impl T for S{}
fn f1(s : &S) -> &(dyn T) {
s
}
fn f2(s : &Box<S>) -> &(dyn T) {
s.deref()
}
(The trait Deref is there for slightly easier readability)
The call to deref() operates over &self, so having &Box<S> is sufficient to call it. It simply returns &S, and since that implements T the types check out.
After reading method-call expressions, dereference operator, method lookup, and auto-dereferencing, I thought I had a pretty good understanding of the subject; but then I encountered a situation in which I expected auto-dereferencing to happen, when in fact it didn't happen.
The example is as follows.
#[derive(Clone, Copy, Debug)]
struct Foo();
impl Into<&'static str> for Foo {
fn into(self) -> &'static str {
"<Foo as Into>::into"
}
}
fn vec_into<F: Copy + Into<T>, T>(slice: &[F]) -> Vec<T> {
slice.iter().map(|x| (*x).into()).collect()
}
fn main() {
let array = [Foo(), Foo(), Foo()];
let vec = vec_into::<_, &'static str>(&array);
println!("{:?}", vec);
}
The code above works, but I thought that the explicit dereferencing (*x).into() in the function vec_into wasn't needed. My reasoning is that, since x: &Foo, then x.into() would try to find methods accepting type &Foo, &&Foo, &mut &Foo, Foo, &Foo, &mut Foo.
This is because there is the chain of dereferencing &Foo → Foo, and for each U in this chain we insert also &U and &mut U.
My intuition is confirmed by the fact that the following code also works, without any explicit dereference.
#[derive(Clone, Copy, Debug)]
struct Foo();
trait MyInto<T> {
fn my_into(self) -> T;
}
impl MyInto<&'static str> for Foo {
fn my_into(self) -> &'static str {
"<Foo as MyInto>::my_into"
}
}
fn vec_my_into<F: Copy + MyInto<T>, T>(slice: &[F]) -> Vec<T> {
slice.iter().map(|x| x.my_into()).collect()
}
fn main() {
let array = [Foo(), Foo(), Foo()];
let my_vec = vec_my_into(&array);
println!("{:?}", my_vec);
}
Here x: &Foo is implicitly dereferenced in order to call the method <Foo as MyInto<&'static str>>::my_into.
A smaller example
Given the above definitions of Foo and MyInto, the code
let result: &str = (&Foo()).my_into()
works, but
let result: &str = (&Foo()).into()
fails to compile with the error
error[E0277]: the trait bound `&str: std::convert::From<&Foo>` is not satisfied
--> src/bin/into.rs:34:33
|
34 | let result: &str = (&Foo()).into();
| ^^^^ the trait `std::convert::From<&Foo>` is not implemented for `&str`
|
= note: required because of the requirements on the impl of `std::convert::Into<&str>` for `&Foo`
Rust performs method lookup exatly as you describe, and it immediately finds a candidate for .into() – the blanket implementation
impl<T, U> Into<U> for T
where
U: From<T>,
{
fn into(self) -> U {
U::from(self)
}
}
This implementation fulfils all the requirements for candidate methods – it is visible, in scope and defined for type &Foo, since it is defined for any type T. Once the compiler has picked this candidate, it notices that the trait bounds on U are not satisfied, and issues the error you see.
The situation for MyInto is completely different, because you don't provide a blanket implementation based on From. If you do, you will get the same error.
It could be argued that the compiler should skip the blanket implementation if the trait bounds are not satisfied, and move on with the list of candidate types until it finds a better fit. The language specification is actually not completely clear on that point, but from the error we see it is clear what the compiler actually does.
Interestingly enough, after inspecting the compiler output, it would appear that the Into implementation actually just calls a method of the trait From. What it's looking for is std::collections::From<&Foo> for &str. Therefore, if we implement that trait, the compiler will indeed find our function, and execute it.
Using the following declarations:
#[derive(Clone, Copy, Debug)]
struct Foo();
impl std::convert::From<&Foo> for &str {
fn from(f: &Foo) -> &'static str {
"A &str"// This could be replaced with an actual implementation
}
}
Your code works as you wanted:
let result: &str = (&Foo()).into();// No compiler errors.
The original code you wanted does work, and it isn't actually hard to implement.
I have a trait with an associated type:
pub trait Speak {
type Error;
fn speak(&self) -> Result<String, Self::Error>;
}
An implementation of that trait:
#[derive(Default)]
pub struct Dog;
impl Speak for Dog {
type Error = ();
fn speak(&self) -> Result<String, Self::Error> {
Ok("woof".to_string())
}
}
And a function returning an instance of that implementation:
pub fn speaker() -> impl Speak {
Dog::default()
}
I know that in this example I could just use Dog as the return type, but in my actual code I have to use impl Speak instead (the above function is in fact generated by a macro).
As I understand it, the impl Trait notation lets the compiler figure out which concrete type is actually returned, so I would expect the following function to compile correctly because speaker() returns a Dog and that Dog::Error is the type ():
fn test() -> Result<String, ()> {
speaker().speak()
}
playground
Instead, I get the following error:
error[E0308]: mismatched types
--> src/lib.rs:21:5
|
20 | fn test() -> Result<String, ()> {
| ------------------ expected `std::result::Result<std::string::String, ()>` because of return type
21 | speaker().speak()
| ^^^^^^^^^^^^^^^^^ expected (), found associated type
|
= note: expected type `std::result::Result<_, ()>`
found type `std::result::Result<_, <impl Speak as Speak>::Error>`
It is as if the compiler could not (at this point) infer the return type of the speaker function.
Who is missing something something, the compiler or myself?
Use -> impl Speak<Error = ()> as the return type of speaker().
The problem is that the compiler needs, from the signature alone, enough information that the caller can actually use the function. If you just return impl Speak, then the compiler knows that speak() returns a Result<String, ???> - the error type isn't known, and thus the compiler issues an error.
The compiler cannot infer anything here. It cannot infer the error type from the call site, because impl Trait in return position doesn't allow inference from the call site. It cannot infer the error type from the implementation, because that would mean whether the caller type-checks depends on the implementation, and that's not how impl Trait works. The caller must always type-check in presence of only the information of the signature; the concrete type is only plugged in after that.
You are.
You never specified the associated Error type, so you're not allowed to assume anything about it. Even if it really is (), the compiler won't allow you to use that knowledge. To solve this, just specify what Error is:
pub fn speaker() -> impl Speak<Error = ()> {
Dog::default()
}