Q: Why does a standard error need to be boxed in main, but not an io error? Take the following code snippet:
fn main() -> std::result::Result<(), std::io::Error> {
    println!("Bonjour le Monde");
    Ok(())
}

This is perfectly fine code, but I wondered what would happen if I changed the type of the error to std::error::Error:
fn main() -> std::result::Result<(), std::error::Error> {
    println!("Bonjour le Monde");
    Ok(())
}

This is not good anymore:

error[E0277]: the size for values of type (dyn std::error::Error + 'static) cannot be known at compilation time

I fixed it like this:
fn main() -> std::result::Result<(), Box<dyn std::error::Error>> {
    println!("Bonjour le Monde");
    Ok(())
}

So why does the main function require Box<> for standard errors, but not for io errors?

A: std::error::Error is a trait included in the standard library. As Rust points out, it cannot determine how much memory will be need to store an Error at compiled time since a structure of any size could potentially be used. As such, you need to use some form of indirection, such as a reference, a pointer, or, in this case, a Box. See TRPL Ch.17 for more information about using trait objects.
In contrast, std::io::Error is a structure from the std::io module (which also happens to implement std::error::Error). Rust does know large this structure is, so it can create a monomorphization of Result that uses it.
