93

In many languages, assignments are legal in conditions. I never understood the reason behind this. Why would you write:

if (var1 = var2) {
  ...
}

instead of:

var1 = var2;
if (var1) {
  ...
}

?

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
lajos
  • 25,525
  • 19
  • 65
  • 75
  • 2
    Is this question really so language-agnostic? Which languages are affected by this? I know C and C++ are, what else? – Wolf Apr 23 '19 at 09:33
  • What other languages besides C and C++ allow this (without a compile-time or run-time error)? PHP? Perl? – Peter Mortensen Jun 26 '22 at 15:26
  • ***Note***: The canonical question for the common ***programming error*** associated with this (unintentional assignment) is *[Variable assignment in "if" condition](https://stackoverflow.com/questions/17681535/)* (even if it is from 2013). – Peter Mortensen Jun 30 '22 at 13:39
  • As expected, there is also [a question from 2008](https://stackoverflow.com/questions/399792/inadvertent-use-of-instead-of) (but search engines don't like to point to it). – Peter Mortensen Jun 30 '22 at 17:27
  • Tell me about it. Just wasted 1 hour as there isnt even a warning in vscode that I had used = instead of == for a normal conditional test. – Johncl Feb 15 '23 at 14:52

12 Answers12

129

It's more useful for loops than if statements.

while(var = GetNext())
{
  ...do something with 'var' 
}

Which would otherwise have to be written

var = GetNext();
while(var)
{
  ...do something
  var = GetNext();
}
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Gerald
  • 23,011
  • 10
  • 73
  • 102
  • 14
    I agree - but would write: while ((var = GetNext()) != 0) { ... } without thinking twice, partly because GCC complains if I don't with my default compilation options. – Jonathan Leffler Oct 08 '08 at 22:08
  • 1
    True. Assuming you're using a language that supports for-loop declarations, and you're not already using var outside of the loop. – Gerald Oct 24 '12 at 16:02
  • 1
    in Some languages, like ANSI C, scoping rules don't permit this approach, @KerrekSB – wirrbel Oct 09 '13 at 16:30
  • 7
    @wirrbel: You mean ANSI C from what, 1989? Like when they had steam engines and punch cards? That may be the case, but is that relevant? – Kerrek SB Oct 09 '13 at 18:18
  • Is this a bad practice? My javascript linter gives me warning: 'expected conditional expression and instead saw an assignment' when I do `while(item = stream.read()) { ... }` – mc9 Jun 19 '15 at 04:32
  • 3
    That warning is not because there is anything wrong with an assignment in a while, but because doing an assignment when you really meant to do a comparison is a common bug. If you want to get rid of the warning you can do the JS equivalent of what @Jonathan Leffler suggests. Personally I would probably do that anyway because of how much I mistrust the JavaScript truthy rules :) It should also be noted in some languages like C# and Java that is the only way. – Gerald Jun 19 '15 at 09:16
39

I find it most useful in chains of actions which often involve error detection, etc.

if ((rc = first_check(arg1, arg2)) != 0)
{
    report error based on rc
}
else if ((rc = second_check(arg2, arg3)) != 0)
{
    report error based on new rc
}
else if ((rc = third_check(arg3, arg4)) != 0)
{
    report error based on new rc
}
else
{
    do what you really wanted to do
}

The alternative (not using the assignment in the condition) is:

rc = first_check(arg1, arg2);
if (rc != 0)
{
    report error based on rc
}
else
{
    rc = second_check(arg2, arg3);
    if (rc != 0)
    {
        report error based on new rc
    }
    else
    {
        rc = third_check(arg3, arg4);
        if (rc != 0)
        {
            report error based on new rc
        }
        else
        {
            do what you really wanted to do
        }
    }
}

With protracted error checking, the alternative can run off the RHS of the page whereas the assignment-in-conditional version does not do that.

The error checks could also be 'actions' — first_action(), second_action(), third_action() — of course, rather than just checks. That is, they could be checked steps in the process that the function is managing. (Most often in the code I work with, the functions are along the lines of pre-condition checks, or memory allocations needed for the function to work, or along similar lines).

Jonathan Leffler
  • 730,956
  • 141
  • 904
  • 1,278
  • Yeah, I actually did just this to avoid excessive nesting and then searched online to see if it was a common practice. In the end I decided against it because I actually only had one else case, but this seems like a legitimate reason to put the assignment in the condition. – Ibrahim Oct 01 '13 at 18:28
  • Coworkers hate it! To me, at least in most cases, is far more maintainable than a giant tree or many returns. I prefer single return from a function... – Nathan Feb 03 '17 at 00:50
  • a reasonable example, the acompanion blocks make the pattern obvious, self-explanatory. But, well, the in-block ("pseudo") code looks a bit ugly, and the inverse style (placing *what you really wanted to do* at the end) isn't that great. – Wolf Apr 23 '19 at 09:39
31

It's more useful if you are calling a function:

if (n = foo())
{
    /* foo returned a non-zero value, do something with the return value */
} else {
    /* foo returned zero, do something else */
}

Sure, you can just put the n = foo(); on a separate statement then if (n), but I think the above is a fairly readable idiom.

Chris Young
  • 15,627
  • 7
  • 36
  • 42
  • 4
    I think you should enclose it in parenthesis or gcc will complain with: warning: suggest parentheses around assignment used as truth value [-Wparentheses] Like this: if ((n = foo())) { // do something with the return value } else { // foo returned zero/NULL, do something else } – Fabio Pozzi Jul 17 '15 at 12:41
26

It can be useful if you're calling a function that returns either data to work on or a flag to indicate an error (or that you're done).

Something like:

while ((c = getchar()) != EOF) {
    // process the character
}

// end of file reached...

Personally it's an idiom I'm not hugely fond of, but sometimes the alternative is uglier.

Michael Burr
  • 333,147
  • 50
  • 533
  • 760
15

GCC can help you detect (with -Wall) if you unintentionally try to use an assignment as a truth value, in case it recommends you write

if ((n = foo())) {
   ...
}

I.e. use extra parenthesis to indicate that this is really what you want.

JesperE
  • 63,317
  • 21
  • 138
  • 197
  • 3
    Good to know. I hope your co-workers read it in the same manner. – Wolf Apr 23 '19 at 09:53
  • 1
    Me? Goodness, I would never try to use assignment as a truth value. I was referring to what GCC recommends. – JesperE May 24 '19 at 05:51
  • GCC can also compile [Fortran](https://en.wikipedia.org/wiki/Fortran). Perhaps be explicit about the programming language? – Peter Mortensen Jul 19 '20 at 06:20
  • I much prefer the explicit `if ((n = foo()) != 0)` to avoid quite so many consecutive close parentheses, but I admit that you can increase the number of consecutive `)` by notations such as `if ((n = foo2(a, somefunc(b, sizeof(SomeType)))) != 0)` – Jonathan Leffler Apr 13 '22 at 14:43
10

The idiom is more useful when you're writing a while loop instead of an if statement. For an if statement, you can break it up as you describe. But without this construct, you would either have to repeat yourself:

c = getchar();
while (c != EOF) {
    // ...
    c = getchar();
}

or use a loop-and-a-half structure:

while (true) {
    c = getchar();
    if (c == EOF) break;
    // ...
}

I would usually prefer the loop-and-a-half form.

Greg Hewgill
  • 951,095
  • 183
  • 1,149
  • 1,285
  • 1
    For a `while` loop, prefer using: `do { c = getchar(); ... } while (c != EOF);` – Scott S Nov 01 '18 at 17:25
  • And `for` loops can be used instead: `for (char c = getchar(); c != EOF; c= getchar()) { /* do something with c */ }` -- this is the most concise, and `for` always says 'loop' to me, stylistically. Small downside is that you have to specify the function returning `c` twice. – Scott S Nov 01 '18 at 17:29
4

The short answer is that expression-oriented programming languages allow more succinct code. They don't force you to separate commands from queries.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Hugh Allen
  • 6,509
  • 1
  • 34
  • 44
  • 1
    It is a constant fight to enforce coding without assignments in if(), while(), and other similar statements. C/C++ are notorious for side effects of such that often turn out to be bugs, especially when we add the ++ and -- operators. – Alexis Wilke Sep 04 '12 at 00:02
  • 1
    The first link is useless: C or C++ seem not to be such a language. – Wolf Apr 23 '19 at 09:58
3

In PHP, for example, it's useful for looping through SQL database results:

while ($row = mysql_fetch_assoc($result)) {
    // Display row
}

This looks much better than:

$row = mysql_fetch_assoc($result);
while ($row) {
    // Display row
    $row = mysql_fetch_assoc($result);
}
Paige Ruten
  • 172,675
  • 36
  • 177
  • 197
  • 5
    This also has the neat side-effect of $row not living beyond that loop and therefore being eligible for garbage collection sooner (in languages that do GC anyway). – Darren Greaves Sep 30 '08 at 05:39
2

The other advantage comes during the usage of GDB.

In the following code, the error code is not known if we were to single step.

while (checkstatus() != -1) {
    // Process
}

Rather

while (true) {
    int error = checkstatus();
    if (error != -1)
        // Process
    else
        // Fail
}

Now during single stepping, we can know what was the return error code from the checkstatus().

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
plasmixs
  • 156
  • 5
0

I find it very useful with functions returning optionals (boost::optional or std::optional in C++17):

std::optional<int> maybe_int(); // function maybe returns an int

if (auto i = maybe_int()) {
    use_int(*i);
}

This reduces the scope of my variable, makes code more compact and does not hinder readability (I find).

Same with pointers:

int* ptr_int();

if (int* i = ptr_int()) {
    use_int(*i);
}
Julien-L
  • 5,267
  • 3
  • 34
  • 51
  • By the way, if you want to limit the scope of variables, you can use if with initializers :) – cigien Oct 25 '20 at 04:37
0

I used it today while programing in Arduino (Subset of C++ language).

Case

I have a transmitter and a receiver. The transmitter wants to send the data until it is received. I want to set a flag when the process is done.

while (!(newtork_joined = transmitter.send(data))) {
Serial.println("Not Joined");
}

Here:

  • "transmitter.send" - returns true when the transmission is successful
  • "newtork_joined " - is the flag I set when successful

Result

  1. When the transmission is not successful, the flag is not set, while loop is true, it keeps executing

  2. When successful, the flag is set, while loop is false, we exit

Isn't beautiful?

Priteem
  • 11
  • 4
  • Re *"in Arduino (C language"*: Arduino's programming language is for the most part a subset of C++ (not C)—though it is not entirely clear what it is. For instance, [it has class 'string'](https://stackoverflow.com/questions/7383606/converting-an-int-or-string-to-a-char-array-on-arduino/7391187#7391187) (e.g., operator "+= " defined for it). This is definitely not C. – Peter Mortensen Jun 26 '22 at 15:29
  • @PeterMortensen, you are right. Let me edit. – Priteem Jun 30 '22 at 13:20
-4

The reason is:

  1. Performance improvement (sometimes)

  2. Less code (always)

Take an example: There is a method someMethod() and in an if condition you want to check whether the return value of the method is null. If not, you are going to use the return value again.

If(null != someMethod()){
    String s = someMethod();
    ......
    //Use s
}

It will hamper the performance since you are calling the same method twice. Instead use:

String s;
If(null != (s = someMethod())) {
    ......
    //Use s
}
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131