Changing one element of an array makes it not convert to slice

This is essentially the same issue as in this recent post. Each function has its own unique unnameable type, which is known only to the compiler. The function type fn() -> Box<dyn ImgOp + 'static> is different from the type of the specific functions Checkergradient::new or Gradient::new, but the latter can be coerced into the former.

Type coercion means that, in certain contexts, if a type T is expected but type R is provided, and there is a coercion from T to R, then the code typechecks.

In the first case, all items of the array must have a common type T, but have in fact different function types. This makes the type checker search for a coercion to a common type, and it finds it: it is the function type fn() -> Box<dyn ImgOp + 'static>. Now the array is well-typed as [fn() -> Box<dyn ImgOp + 'static>; 2] which can dereference to &[fn() -> Box<dyn ImgOp + 'static>], and the code successfully typechecks.

In the second case, all elements of the array already have the same type, so its type is successfully inferred as [CheckerGradient::new; 2]. Note that once the type is fully inferred, it cannot be changed. So you pass &[CheckerGradient::new; 2] to a function expecting &[fn() -> Box<dyn ImgOp + 'static>], which means that this will typecheck only if the former coerces to the latter. But if you look at the list of valid coercions, there is nothing which would make it legal, which is exactly what the compiler complains about. Note that coercions are transitive, but there is no coercion [T; N] to [U; N], even if T coerces to U.

9 Likes