When is it a good idea to declare type of variable when not needed?

I have been considering when, if ever, I should add type declarations for variables even though the compiler can infer the type, to help make the code easier to read.

Do you do this often? When (if ever) is it a good idea? On the whole, I am not convinced it's a good idea, but maybe there are situations where it is helpful?

I wouldn't say I do this often, but I have done it. Most frequently when maintaining or refactoring and the type wasn't immediately apparent or making it explicit sped things up as I didn't have to think about it when bouncing between files/functions. It can be defensive to add suffixes or ascription for floats or especially integers, as those have a default type if inference otherwise fails.

There was a post on this forum recently where a Vec<&mut Something> was collected, and I thought that was a decent place to leave the ultimately-unneeded ascription, as a vector of mutable references is a fairly uncommon thing.

1 Like

I agree that I don't do it often either. I think I probably treat them like comments. Comments always have a place, even in well-written code. A type annotation is just a comment the compiler happens to understand.

1 Like

I used to have a few unnecessary type annotations, but I haven't needed to do that since I started using VSCode with the Rust analyzer. I find the inlay hints to be better than anything I add.

1 Like

I also never type annotations and instead rely on rust-analyzer to provide them. I used to fear it'll make code hard to read on GitHub, for instance, but apparently it works quite well.

1 Like

I don't recall any situation where I felt I need to write types in let when the were not required by the compiler.

Rust is generally strict enough that I can rely on the compiler, and not need to be mindful of types "manually". In case code is unclear, there are other methods of making it clearer (naming variables better, or splitting code into smaller functions, which also adds explicit types as a side effect).

The only place where I'm afraid types are too subtle and easy to get wrong is numeric as casts and transmute. Transmute is fixable with turbofish syntax, and as is a lost cause. Numeric code that requires as casts is already verbose, and doubling the boilerplate would be even worse for readability. I don't think it'd prevent unexpected truncation anyway.

1 Like

Definitely agreed that inside a method, usually leaving off the type annotations is the right choice. My general suggestion is that when you start wanting more type annotations to treat is as a refactoring hint -- maybe you can pull part of the function out into a separate function, which would itself have type annotations for the parameters and return types, rather than needing a bunch inside the function. (That's not always better, and annotations might be the right choice instead, but it's worth thinking about.)

The big place where I'd suggest over-annotating a bit is in unsafe code. As an example of me failing to do that in the past,

That compiled and kinda worked back on an old version of rustc, but it was totally UB. And because in unsafe code there's more freedom (aka ability to shoot yourself in the foot), it's less likely to have something will fail to compile when it's incorrect. And just replacing that _ with the type I expected made it stop compiling, and helped me find the real problem.

So like kornel said earlier, the hairier unsafe stuff is a great place to be more explicit than the compiler will let you get away with.

5 Likes

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.