skip to content

Operators & Cascades

Dart's operators: integer division ~/, compound assignment, the conditional ?:, the cascade .. for chained calls on one receiver, and dot shorthands like .parse. Interviewers probe precedence traps.

part ofDartoverview, primer and where to startread it →
on this pageshow

explore

questions

4

In Dart, how do the /, ~/ and % operators differ on int operands, and what do ~/ and % give for negative numbers?

level: juniorimportance: must knowfreq 52%

answer

  1. one of them never returns int
  2. rounding toward zero, not down
  3. Euclidean, never negative
  4. remainder() keeps the dividend's sign
  5. n /= 2 on an int

basics

~20 s

In Dart, / always returns a double, ~/ returns an int truncated toward zero (-7 ~/ 2 is -3), and % is Euclidean modulo whose result is never negative (-5 % 3 is 1); remainder() gives the sign-following remainder.

solid answer

~40 s

`/` is declared as `double operator /(num other)`, so `4 / 2` is `2.0`, never an `int`. `~/` is truncating division: it returns an `int` rounded toward zero, so `-7 ~/ 2` is `-3`, not `-4`. `%` is Euclidean modulo: the result is always between `0` and `b.abs()`, so `-5 % 3` is `1`, which makes it the right tool for wrapping an index. The truncated remainder is the `remainder()` method, where `(-5).remainder(3)` is `-2`, and it pairs with `~/` so that `a == (a ~/ b) * b + a.remainder(b)`. Compound forms follow the same types: `n /= 2` on an `int` does not compile, `n ~/= 2` does.

code

dart · 15 lines
dart
void main() {
  print(7 / 2); // 3.5
  print(4 / 2); // 2.0 (a double)
  print(-7 ~/ 2); // -3 (toward zero)
  print(-5 % 3); // 1 (Euclidean)
  print((-5).remainder(3)); // -2

  const pageCount = 5;
  var page = 0;
  page = (page - 1) % pageCount; // wraps to 4
  print(page);

  var seconds = 135;
  print('${seconds ~/ 60}m ${seconds % 60}s'); // 2m 15s
}

go deeper

for a junior

Recall the three operators and their result types: / gives a double, ~/ gives an int, % gives the modulo. Have one negative example ready for each.

for a middle

Explain truncation toward zero versus Euclidean modulo, name remainder() as the sign-following alternative, and show why n /= 2 on an int is a compile error.

for a senior

Show where the choice bites in real code: wrapping carousel or ring-buffer indices with %, splitting signed durations with ~/ and remainder(), and guarding ~/ against a zero divisor.

for a principal

Frame numeric operators as a correctness and portability concern: lint for toInt() after /, and remember that web builds use JavaScript numbers, so bitwise and large-integer results can differ from native.

## The three division-family operators Dart gives integer arithmetic **three** related operators, and interviewers use them to check whether a candidate has actually written Dart rather than guessing from another language. | Operator | Name | Result type | `7 op 2` | `-7 op 2` | |---|---|---|---|---| | `/` | division | always `double` | `3.5` | `-3.5` | | `~/` | truncating division | always `int` | `3` | `-3` | | `%` | Euclidean modulo | `int` if both operands are `int`, else `double` | `1` | `1` | - **`/` never returns an `int`.** `num` declares `double operator /(num other)`, so even `4 / 2` is the double `2.0`. Code that expects an index or a count from `/` does not compile once the result reaches an `int` variable. - **`~/` truncates toward zero.** The SDK documents it as division "where a fractional result is converted to an integer by rounding towards zero". `-7 ~/ 2` is `-3`, not `-4`: it is not a floor. - **`%` is Euclidean, not a truncated remainder.** The result is never negative: `-5 % 3 == 1` and `5 % -3 == 2`. The SDK doc comment states the rule as `a == b * q + r` with `0 <= r < b.abs()`. ## `remainder()` is the other remainder Because `%` is Euclidean, Dart keeps the truncated remainder as a method, `num.remainder`. Its sign follows the dividend: `(-5).remainder(3) == -2`. It pairs with `~/` through the identity documented on `~/`: ```dart a == (a ~/ b) * b + a.remainder(b) ``` The parentheses in `(-5).remainder(3)` matter. Member access binds tighter than unary minus, so `-5.remainder(3)` means `-(5.remainder(3))`. It happens to give the same `-2` here, but only by coincidence. When to use which: 1. **Wrapping an index into a range** (a carousel page, a ring buffer, a weekday offset): use `%`. `(index - 1) % length` stays in `0..length-1` even when `index` is `0`. 2. **Splitting a quantity into whole units and a rest** (seconds into minutes and seconds): use `~/` for the units and `%` for the rest when the value is non-negative. With negative values, pair `~/` with `remainder()`, because `~/` rounds toward zero while `%` is Euclidean, so the two only pair up for non-negative values. 3. **Anything fractional** (averages, ratios, progress): use `/` and keep a `double`. ## Edge cases worth knowing - **Doubles in `~/`**: if either operand is a `double`, the other is converted and the result is `(a / b).truncate()`. `7.5 ~/ 2` is `3`, still an `int`. - **Dividing by zero**: `5 / 0` is `double.infinity` and does not throw. `5 ~/ 0` throws an `UnsupportedError`, because no finite integer exists; the deprecated `IntegerDivisionByZeroException` also implements `UnsupportedError`, so catching that type covers both. - **The `use_truncating_division` lint** flags `(a / b).toInt()` and asks for `a ~/ b`, which is shorter and can be faster on some platforms. ## Increment and compound assignment Every binary arithmetic operator has a compound form: `+=`, `-=`, `*=`, `/=`, `~/=`, `%=`, plus the bitwise and shift forms `&=`, `|=`, `^=`, `<<=`, `>>=`, `>>>=`. `a op= b` means `a = a op b`, and the result must still be assignable to `a`'s type. That makes one line a classic trap: ```dart int n = 10; n /= 4; // compile-time error: double can't be assigned to int n ~/= 4; // fine: n == 2 ``` Prefix and postfix increment differ only in the value of the expression. `++a` increments first and yields the new value; `a++` yields the old value and then increments. After `var a = 0; var b = a++;`, `a` is `1` and `b` is `0`. ## Bitwise and shift operators in brief On `int`, Dart has `&`, `|`, `^`, `~` (complement), `<<`, `>>` (arithmetic shift, keeps the sign) and `>>>` (unsigned shift, fills with zeros; language version 2.14 or later). `(-9) >>> 2` is positive, while `-9 >> 2` stays negative. A negative shift amount is an error. Typical Flutter-side uses are packing flags into an `int` and masking bits out of a raw value. On the web, integers are JavaScript numbers and these operators work on 32 bits, so results for large or negative operands can differ from native. ## What an interviewer listens for - That `/` always yields a `double` and `~/` always yields an `int`. - That `~/` truncates toward zero and that `%` is never negative, with a concrete negative example. - That `remainder()` exists for the truncated case. - That `n /= x` on an `int` does not compile, and `~/=` is the fix.

  • Why does `int n = 10; n /= 4;` fail to compile in Dart, and what do you write instead?
    A compound assignment `n /= 4` means `n = n / 4`, and `/` always produces a `double`. A `double` cannot be assigned to an `int` variable, so the analyzer rejects it. Use `n ~/= 4` for integer division (giving `2`), or declare `n` as `double` or `num` if you really want the fraction.
  • What happens in Dart when you divide by zero with / and with ~/?
    `5 / 0` follows floating-point rules and yields `double.infinity` without throwing. `5 ~/ 0` must produce an `int`, and no finite integer exists, so it throws an `UnsupportedError`. The old `IntegerDivisionByZeroException` is deprecated and implements `UnsupportedError`, so catching `UnsupportedError` covers both. Better still, check the divisor first.
  • When would you use remainder() instead of % in Dart?
    Use `remainder()` when you need the result to pair with `~/` for possibly negative values, for example splitting a signed duration into whole minutes and the rest so that `(a ~/ b) * b + a.remainder(b) == a`. Use `%` when you need a non-negative result, such as wrapping a list index or a day-of-week offset.

saying these in an interview costs you the question

  • Thinks 4 / 2 gives the int 2 in Dart
  • Says ~/ rounds down, so -7 ~/ 2 is -4
  • Expects -5 % 3 to be -2 as in a truncated remainder
  • Uses (a / b).toInt() everywhere instead of ~/
  • Believes n /= 2 on an int silently truncates
open as a page

In Dart, what does the cascade .. do, what value does a cascade expression produce, and how does it differ from method chaining?

level: middleimportance: must knowfreq 58%

basics

~10 s

Dart's cascade receiver..member runs a sequence of calls, setters or index assignments on one object, discards each result, and evaluates to the receiver itself, so any API gets fluent style without methods returning this.

open as a page

In Dart 3.10 and later, how does a dot shorthand like .start or .parse('8080') resolve, and where does it fail to compile?

level: middleimportance: should knowfreq 40%

basics

~20 s

A Dart dot shorthand such as .start omits the type name and is resolved against the context type, the type expected at that position; it fails where no context exists, like var x = .a, or where that type lacks the member.

open as a page

In Dart, why do expressions like -1.abs(), await fetchUsers().length and cond ? a : b..add(x) not parse the way they read?

level: seniorimportance: should knowfreq 30%

basics

~20 s

In Dart, member access binds tighter than prefix operators, so -1.abs() is -(1.abs()) and await f().length awaits the length; a cascade binds looser than ?:, so b..add(x) after a conditional mutates whichever branch was chosen.

open as a page