The Software Purist |

Jan/11

24

Var/Auto is Ugly and in Some Cases, Downright Evil.

In this post, I’m going to describe a set of keywords, which effectively serve the same purpose. In C#, there is the var keyword. In C++, there is the auto keyword. Effectively, what they let you do is to automatically inter the type of a variable from the context on the right hand side. Here’s an example, in C#:


var myVar = new MyClass();

and an example in C++:


auto myVar = MyClass();

The problem I have is, while this allows you a tremendous amount of flexibility, in saving redundant typing, it also potentially nullifies some of the benefits of using a language which supports type safety, because you can do some pretty nasty things which destroy the readability and can potentially introduce some subtle bugs. Worse still, tools like Resharper encourage, potentially poor usages of var. Here’s another example of the var problem:


var myVar = new MyClass().DoThis().DoThat().DoSomethingElse().NowGet();

Ok, what’s the type? Don’t know? Me neither. I find this problematic. As such, as I’d like to establish some guidelines for better usage of var/auto, taking some common use cases. I’ll probably switch back and forth with examples from C++ and C#, just to illustrate the point. Here we go:

1) Usage case 1:


var x = new Y();

I consider this usage a minor evil, even though some like it a lot. Here’s my major problem: You have type inference on the wrong side. The point of using a language which supports type safety, is, well, you support the type system and let it help you. If you don’t want that, may I suggest a language that is dynamically typed? It would be nice, if var, instead worked this way:


Y x = new infer-this-type();

As a general rule, we would prefer to infer the type that’s on the right hand side, not the left. This feature is not available in a lot of languages, so for now, I would simply suggest not using var. Go with the old:


Y x = new Y();

2) Usage case 2:


var x = GetY();

Again, with this one, I prefer not to use var, for the same reason as before, with an added caveat. First, of all, you should avoid using var to avoid typing a simple type. Secondly, you can’t even figure out what the type is from reading the code. Not good. On a scale of 1 to evil, I consider this a significant evil.

3) Usage case 3:


for (typename std::vector::const_iterator iter = container.begin(); iter != container.end(); ++iter)
{
...
}

Becomes:


for (auto iter = container.begin(); iter != container.end(); ++iter)
{
...
}

In this case, I can justify using var/auto, so I consider using auto a minor evil. However, C++ has a better mechanism for doing this. It’s called typedef. For example:


typedef typename std::vector::const_iterator MyIter;

for (MyIter iter = container.begin(); iter != container.end(); ++iter)
{
...
}

So, in C++, I have trouble finding ANY usage, where I really like auto. However, in C#, there is no typedef, so I’m fine with usage of var, in the case of complicated nested classes.

Conclusion

Concluding, as you figured, I don’t like the usage of var or auto much. In a lot of cases, it is a minor evil. However, there are some major concerns: Readability and subverting some of the redundant checking that the type system supports. In C#, it is sometimes useful to save a lot of typing, due to the lack of the typedef keyword, while in C++, it is rarely useful. In a future article, I will tackle C#’s dynamic keyword, which I dislike far more than var. Stay tuned.

RSS Feed

2 Comments for Var/Auto is Ugly and in Some Cases, Downright Evil.

RM | April 21, 2011 at 10:59 am

I don’t really see the difference between use cases 2 and 3 here. In use case 3 you say that “In this case, I can justify using var/auto, so I consider using auto a minor evil” but fail to explain why – is it because you are familiar with the return type of the function you’re calling so would rather not type it out?

The point of auto, as i can see it, is that you are aware of the code’s context – most people writing code can right click a function call and go and look at the signature if they’re not sure of the type. Staring at a piece of code like “var myVar = new MyClass().DoThis().DoThat().DoSomethingElse().NowGet();” out of context is not a real situation faced by a programmer.

Also, I can’t help but point out that inferring the type on the right hand side of the assignment wouldn’t make any sense for a language where classes are reference types, because the compiler would have to guess which constructor out of every subtype of Y you want to call.

Author comment by softwarepurist | June 22, 2011 at 5:36 am

Hey RM. I get what you’re saying, let me elaborate.

Case 3 was sort of a gotcha that I didn’t explain well. In C++, the methods that return iterators, begin() and end(), are overloaded so that they can return both const and non-const versions. If you use auto which do you get? I’m guessing the const version. The issue with auto is it’s “fine” unless the compiler isn’t inferring the type the same way you’re inferring the type. It’s not always so obvious and the code has the risk of looking kind of non-uniform. I’m okay using it in case 3; I don’t think it really smells of abuse as much as some of the other cases.

As for this being out of context:

var myVar = new MyClass().DoThis().DoThat().DoSomethingElse().NowGet();

You have a good point about it being out of context. I can’t think of a great example right now, but I have certainly seen code like in production. Let’s say DoThis() returns an instance of class B, DoThat() returns an instance of class C, DoSomethingElse() returns an instance of D and NowGet() returns an instance of class E.

The problem here is it’s really not obvious that you’re getting an E. Moreso, maybe you weren’t expecting to get an E in this case. You’re not really using the type system to its fullest.

My dislike of the use of var/auto has to do with the fact that you’re working in a strongly-typed language. You’re supposed to get compile errors when you write code that doesn’t meet the assumptions you had. When you use var/auto, you reduce the compiler’s ability to help you in these cases. In some cases, it’s relatively harmless, because the compiler will just bark at a later line. In other cases, it can lead to code with subtle errors.

“Also, I can’t help but point out that inferring the type on the right hand side of the assignment wouldn’t make any sense for a language where classes are reference types, because the compiler would have to guess which constructor out of every subtype of Y you want to call.”

It would have to be a language feature, of course. Inferring on the right-hand side is not unheard of. Java can do it, for example. Here’s one example:

public class Utility
{
public static ArrayList newArrayList()
{
return new ArrayList
();
}
}

… to use …

ArrayList arrayList = Utility.newArrayList();

This is kind of the idea I had in mind, except it would have to invoke the constructor on the type rather than just passing it as a generic argument to another, so it’s a slightly different concept, but the idea is similar.

Leave a comment!

<<

>>

Find it!