Closed dan-cooke closed 1 year ago
The compiler is trying to tell you that the type you are trying to convert to doesn't implement a into or a try_into/try_from fn.
Working example
#[tracing::instrument(
name = "Adding a new subscriber",
skip(form, pool),
fields(
email = %form.email,
name = %form.name
)
)]
pub async fn subscribe(form: web::Form<FormData>, pool: web::Data<PgPool>) -> HttpResponse {
let new_subscriber: NewSubscriber = match form.0.try_into() {
Ok(form) => form,
Err(_) => return HttpResponse::BadRequest().finish(),
};
match insert_subscriber(&pool, &new_subscriber).await {
Ok(_) => HttpResponse::Ok().finish(),
Err(_) => HttpResponse::InternalServerError().finish(),
}
}
NewSubscriber (see how it has a TryFrom impl)
use crate::domain::SubscriberEmail;
use crate::domain::SubscriberName;
use crate::routes::FormData;
pub struct NewSubscriber {
pub email: SubscriberEmail,
pub name: SubscriberName,
}
impl TryFrom<FormData> for NewSubscriber {
type Error = String;
fn try_from(value: FormData) -> Result<Self, Self::Error> {
let name = SubscriberName::parse(value.name)?;
let email = SubscriberEmail::parse(value.email)?;
Ok(Self { email, name })
}
}
Just make sure your SubscribeBody has a TryFrom or TryInto implemented for it. And I think it's a good idea to always have the type marked for into/try_into lines.
@rj-jones that is not the case unfortunately I do infact have a TryFrom implementation for that specific struct.
you can see in my post that I am calling try_into
and it works, when I explicitly annotate the variable with NewSubscriber
- so I don't think the issue is a missing trait definition.
Here is my TryFrom impl
use crate::routes::SubscribeBody;
use super::subscriber_email::SubscriberEmail;
#[derive(Debug)]
pub struct NewSubscriber {
pub email: SubscriberEmail,
}
impl TryFrom<SubscribeBody> for NewSubscriber {
type Error = String;
fn try_from(value: SubscribeBody) -> Result<NewSubscriber, Self::Error> {
let email = SubscriberEmail::parse(value.email)?;
Ok(Self { email })
}
}
And I can succesfully do this
let new_subscriber = NewSubscriber::try_from(body.0);
This works perfectly fine, so try_from
is definetly implemented correctly.
The compiler thinks that I am trying to try_into
the type ()
because it is unable to infer NewSubsciber
without an explicit definition
Why can the book can get away with no explicit type hint (let new_subscriber = match...
) ...
wheras in rust 1.7.1 I cannot - I must do let new_subscriber: NewSubscriber = match
. I suspect there has been a change in the rust toolchain
@rj-jones wow okay I have just figured out why this is happening
The difference is this line
--- fn try_from(value: SubscribeBody) -> Result<NewSubscriber, Self::Error> {
+++ fn try_from(value: SubscribeBody) -> Result<Self, Self::Error> {
I think this is happening because technically -> Result<NewSusbcriber...
is not the correct signature for satisfying the TryFrom
trait
TIL: Self
!== TheType
in this context
Wow also this is a real edge case the compier seems to be unable to catch... it throws an error if I try to return any other type, but it sees Self
and NewSubscriber
as equivalent - even though, it does not satisfy the trait def
Weird
Good catch! I've been staring at this for some time now and that was the only difference I could see. But that shouldn't be causing the issue 🤔...interesting. Since you mentioned you were new to Rust, it is good practice to use Self in impl's like that. Also, I agree with you about doing let new_subscriber: NewSubscriber = match
, I think this is a good idea for clarity.
The following line
https://github.com/LukeMathWalker/zero-to-production/blob/root-chapter-07-part2/src/routes/subscriptions.rs#L43
This code will not compile, and I will get
Which is very odd - it seems to be trying to convert into the Unit type
()
.If we break down the match block a little bit and do the following
We get the following error
Question
I am quite new to rust, and I have not been following updates for long - has something changed since the book was published with type inference? It seems we can no longer infer the type of
NewSubscriber
here and we must explicitly do_which honestly makes sense to me, the way its done in the book seems like magic to me__