Lvalues and Rvalues - D Programming Language

Menu

- Learn
- Documentation
  - Language Reference
  - Library Reference
  - Command-line Reference
  - Feature Overview
  - Articles
- Downloads
- Packages
- Community
  - Blog
  - Orgs using D
  - Twitter
  - Calendar
  - Discord (community-run)
  - Forums
  - IRC
  - Wiki
  - Issues
  - Contributing
  - Contributors
  - Foundation
  - Security Team
  - Donate
  - Sponsors
- Resources
  - Tour
  - Books
  - Tutorials
  - Tools
  - Editors
  - IDEs
  - run.dlang.io
  - Visual D
  - Acknowledgments
  - D Style
  - Glossary
  - Sitemap

Search
Report a bug
If you spot a problem with this page, click here to create a Bugzilla issue.
Improve this page
Quickly fork, edit online, and submit a pull request for this page. Requires a signed-in GitHub account. This works well for small changes. If you'd like to make larger changes you may want to consider using a local clone.

# Lvalues and Rvalues

The value of every expression is classified as either an lvalue or an rvalue. A simple way of differentiating the two is thinking of lvalues as actual variables (including elements of arrays and associative arrays), and rvalues as temporary results of expressions (including literals).

As a demonstration, the first `writeln()` expression below uses only lvalues and the other one uses only rvalues:

    import std.stdio;

    void main() {
        int i;
        immutable(int) imm;
        auto arr = [ 1 ];
        auto aa = [ 10 : "ten" ];

        /* All of the following arguments are lvalues. */

        writeln(i,          // mutable variable
                imm,        // immutable variable
                arr,        // array
                arr[0],     // array element
                aa[10]);    // associative array element
                            // etc.

        enum message = "hello";

        /* All of the following arguments are rvalues. */

        writeln(42,               // a literal
                message,          // a manifest constant
                i + 1,            // a temporary value
                calculate(i));    // return value of a function
                                  // etc.
    }

    int calculate(int i) {
        return i * 2;
    }

##### Limitations of rvalues

Compared to lvalues, rvalues have the following three limitations.

###### Rvalues don't have memory addresses

An lvalue has a memory location to which we can refer, while an rvalue does not.

For example, it is not possible to take the address of the rvalue expression `a + b` in the following program:

    import std.stdio;

    void main() {
        int a;
        int b;

        readf(" %s", &a);          // ← compiles
        readf(" %s", &(a + b));    // ← derleme HATASI
    }

    Error: a + b is not an lvalue

###### Rvalues cannot be assigned new values

If mutable, an lvalue can be assigned a new value, while an rvalue cannot be:

        a = 1;          // ← compiles
        (a + b) = 2;    // ← derleme HATASI

    Error: a + b is not an lvalue

###### Rvalues cannot be passed to functions by reference

An lvalue can be passed to a function that takes a parameter by reference, while an rvalue cannot be:

    void incrementByTen(ref int value) {
        value += 10;
    }

    // ...

        incrementByTen(a);        // ← compiles
        incrementByTen(a + b);    // ← derleme HATASI

    Error: function deneme.incrementByTen (ref int value)
    is not callable using argument types (int)

The main reason for this limitation is the fact that a function taking a `ref` parameter can hold on to that reference for later use, at a time when the rvalue would not be available.

Different from languages like C++, in D an rvalue cannot be passed to a function even if that function does not modify the argument:

    void print(ref const(int) value) {
        writeln(value);
    }

    // ...

        print(a);        // ← compiles
        print(a + b);    // ← derleme HATASI

    Error: function deneme.print (ref const(int) value)
    is not callable using argument types (int)

##### Using `auto ref` parameters to accept both lvalues and rvalues

As it was mentioned in the previous chapter, `auto ref` parameters of function templates can take both lvalues and rvalues.

When the argument is an lvalue, `auto ref` means by reference. On the other hand, since rvalues cannot be passed to functions by reference, when the argument is an rvalue, it means by copy. For the compiler to generate code differently for these two distinct cases, the function must be a template.

We will see templates in a later chapter. For now, please accept that the highlighted empty parentheses below make the following definition a function template.

    void incrementByTen()(auto ref int value) {
        /* WARNING: The parameter may be a copy if the argument is
         * an rvalue. This means that the following modification
         * may not be observable by the caller. */

        value += 10;
    }

    void main() {
        int a;
        int b;

        incrementByTen(a);        // ← lvalue; passed by reference
        incrementByTen(a + b);    // ← rvalue; copied
    }

It is possible to determine whether the parameter is an lvalue or an rvalue by using `__traits(isRef)` with `static if` :

    void incrementByTen()(auto ref int value) {
        static if (__traits(isRef, value)) {
            // 'value' is passed by reference
        } else {
            // 'value' is copied
        }
    }

We will see `static if` and `__traits` later in the Conditional Compilation chapter.

##### Terminology

The names "lvalue" and "rvalue" do not represent the characteristics of these two kinds of values accurately. The initial letters l and r come from left and right, referring to the left- and the right-hand side expressions of the assignment operator:

- Assuming that it is mutable, an lvalue can be the left-hand expression of an assignment operation.
- An rvalue cannot be the left-hand expression of an assignment operation.

The terms "left value" and "right value" are confusing because in general both lvalues and rvalues can be on either side of an assignment operation:

        // rvalue 'a + b' on the left, lvalue 'a' on the right:
        array[a + b] = a;

Copyright © 1999-2026 by the D Language Foundation | Page generated by Ddoc on Wed Aug 5 15:38:51 2026