This is similar to this question. When you assign future to the newly created Task, and since Task::future is of type Mutex<Pin<Box<dyn Future<Output = ()> + Send>>>, this becomes a constraint for type inference: future should have type Mutex<Pin<Box<dyn Future<Output = ()> + Send>>>.
Now, future actually has the type Mutex<Pin<Box<[some future generated by async block]>>> (let's name [some future generated by async block] Fut from now), not Mutex<Pin<Box<dyn Future<Output = ()> + Send>>>. Pin<Box<Fut>> can coerce to Pin<Box<dyn Future<Output = ()> + Send>> (and also Box<Fut> can coerce to Box<dyn Future<Output = ()> + Send>), but will do so only if the compiler deems this is necessary. On the other hand, Mutex<Pin<Box<Fut>>> cannot coerce to Mutex<Pin<Box<dyn Future<Output = ()> + Send>>>, because coercion isn't recursive (T can coerce to U doesn't mean A<T> can coerce to A<U>).
The important point is, the compiler only checks whether a coercion should be performed at special coercion sites. Instantiation of a struct is a coercion site for each field expression, and so are method calls, but variable declarations let statement without an explicit type are not coercion sites. We can follow the compiler inference:
For the first, working case:
The expression provided for Task::future should have type Mutex<Pin<Box<dyn Future<Output = ()> + Send>>>, i.e. this is its expected type (or, the compiler has an Expectation for it to be of that type).
Since Mutex::new() has the signature fn<T>(T) -> Mutex<T>, and we know it should return Mutex<Pin<Box<dyn Future<Output = ()> + Send>>>, we can conclude that the value of T is Pin<Box<dyn Future<Output = ()> + Send>>. So, its parameter also should have that type, or this is its expected type.
Ditto for Box::pin(), with a signature of fn<T>(T) -> Pin<Box<T>>. Now T is dyn Future<Output = ()> + Send. We do not provide a value of that type: we provide a value of type Fut. But don't give up! Let's trace back and try to find a good coercion. Fut cannot be coerced to dyn Future<Output = ()> + Send directly, but if we go back one step, we see Pin<Box<Fut>> that can be coerced to Pin<Box<dyn Future<Output = ()> + Send>>. This is inside a method call (Mutex::new()) so it can be coerced here. Problem solved.
Now let's follow the second case:
First, we infer the type for the future variable. We have no expectation for it, because no type is specified. We have no expectation for T in Mutex::new() or the one in Box::pin() either. So we can conclude from the value that T for Box::pin() is Fut, and then T for Mutex::new() is Pin<Box<Fut>>. Thus future has type Mutex<Pin<Box<Fut>>>.
Now, when trying to type-check the expression for the field future, we notice it has type Mutex<Pin<Box<Fut>>> while its expected type is Mutex<Pin<Box<dyn Future<Output = ()> + Send>>>. We cannot coerce the first to the second, but neither we can propagate the coercion, because let is not a coercion site. So we declare an error.
The fix is simple: you should insert a coercion site. You can either specify the type for the let future, and make the compiler coerce it there, or specify as _ after Box::pin() (Mutex::new(Box::pin(future) as _), of course specifying the type will also work), forcing the compiler to put a coercion site there (this is quite confusing, because method calls are already coercion sites, but by default the compiler doesn't coerce there because it infers T by the parameter. Putting as _ there is like telling the compiler "Stop! I want to disconnect the return type from the argument's type, it is not necessarily Pin<Box<[parameter type]>>, it only should be coercible (castable, to be precise) to it!").