std.variant - D Programming Language
Description: D Programming Language
Keywords: 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
  - Forums
  - IRC
  - Community Discord
  - Wiki
  - GitHub
  - Issues
  - Get involved
  - 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.

# std.variant

This module implements a discriminated union type (a.k.a. tagged union, algebraic type). Such types are useful for type-uniform binary interfaces, interfacing with scripting languages, and comfortable exploratory programming.
A Variant object can hold a value of any type, with very few restrictions (such as shared types and noncopyable types). Setting the value is as immediate as assigning to the Variant object. To read back the value of the appropriate type T, use the get method. To query whether a Variant currently holds a value of type T, use peek. To fetch the exact type currently held, call type, which returns the TypeInfo of the current value.
In addition to Variant, this module also defines the Algebraic type constructor. Unlike Variant, Algebraic only allows a finite set of types, which are specified in the instantiation (e.g. Algebraic!(int, string) may only hold an int or a string).
Warning: Algebraic is outdated and not recommended for use in new code. Instead, use std.sumtype.SumType.

Credits Reviewed by Brad Roberts. Daniel Keep provided a detailed code review prompting the following improvements: (1) better support for arrays; (2) support for associative arrays; (3) friendlier behavior towards the garbage collector.

License:
Boost License 1.0.
Authors:
Andrei Alexandrescu

Source std/variant.d

Examples:

    Variant a; // Must assign before use, otherwise exception ensues
    // Initialize with an integer; make the type int
    Variant b = 42;
    writeln(b.type); // typeid (int)
    // Peek at the value
    assert(b.peek!(int) !is null && *b.peek!(int) == 42);
    // Automatically convert per language rules
    auto x = b.get!(real);

    // Assign any other type, including other variants
    a = b;
    a = 3.14;
    writeln(a.type); // typeid (double)
    // Implicit conversions work just as with built-in types
    assert(a < b);
    // Check for convertibility
    assert(!a.convertsTo!(int)); // double not convertible to int
    // Strings and all other arrays are supported
    a = "now I'm a string";
    writeln(a); // "now I'm a string"

    // can also assign arrays
    a = new int[42];
    writeln(a.length); // 42
    a[5] = 7;
    writeln(a[5]); // 7

    // Can also assign class values
    class Foo {}
    auto foo = new Foo;
    a = foo;
    assert(*a.peek!(Foo) == foo); // and full type information is preserved

enum auto `maxSize`(Ts...);
  Gives the sizeof the largest type given.
  See Also:
  https://forum.dlang.org/thread/wbpnncxepehgcswhuazl@forum.dlang.org?page=1
  Examples:

      struct Cat { int a, b, c; }

      align(1) struct S
      {
          long l;
          ubyte b;
      }

      align(1) struct T
      {
          ubyte b;
          long l;
      }

      static assert(maxSize!(int, long) == 8);
      static assert(maxSize!(bool, byte) == 1);
      static assert(maxSize!(bool, Cat) == 12);
      static assert(maxSize!(char) == 1);
      static assert(maxSize!(char, short, ubyte) == 2);
      static assert(maxSize!(char, long, ubyte) == 8);
      import std.algorithm.comparison : max;
      static assert(maxSize!(long, S) == max(long.sizeof, S.sizeof));
      static assert(maxSize!(S, T) == max(S.sizeof, T.sizeof));
      static assert(maxSize!(int, ubyte[7]) == 7);
      static assert(maxSize!(int, ubyte[3]) == 4);
      static assert(maxSize!(int, int, ubyte[3]) == 4);
      static assert(maxSize!(void, int, ubyte[3]) == 4);
      static assert(maxSize!(void) == 1);

struct `VariantN`(size_t maxDataSize, AllowedTypesParam...);
  Back-end type seldom used directly by user code. Two commonly-used types using `VariantN` are:
  1. Algebraic: A closed discriminated union with a limited type universe (e.g., Algebraic!(int, double, string) only accepts these three types and rejects anything else).
  2. Variant: An open discriminated union allowing an unbounded set of types. If any of the types in the Variant are larger than the largest built-in type, they will automatically be boxed. This means that even large types will only be the size of a pointer within the Variant, but this also implies some overhead. Variant can accommodate all primitive types and all user-defined types.
  Both Algebraic and Variant share VariantN's interface. (See their respective documentations below.)
  `VariantN` is a discriminated union type parameterized with the largest size of the types stored (maxDataSize) and with the list of allowed types (AllowedTypes). If the list is empty, then any type up of size up to maxDataSize (rounded up for alignment) can be stored in a `VariantN` object without being boxed (types larger than this will be boxed).
  Examples:

      alias Var = VariantN!(maxSize!(int, double, string));

      Var a; // Must assign before use, otherwise exception ensues
      // Initialize with an integer; make the type int
      Var b = 42;
      writeln(b.type); // typeid (int)
      // Peek at the value
      assert(b.peek!(int) !is null && *b.peek!(int) == 42);
      // Automatically convert per language rules
      auto x = b.get!(real);

      // Assign any other type, including other variants
      a = b;
      a = 3.14;
      writeln(a.type); // typeid (double)
      // Implicit conversions work just as with built-in types
      assert(a < b);
      // Check for convertibility
      assert(!a.convertsTo!(int)); // double not convertible to int
      // Strings and all other arrays are supported
      a = "now I'm a string";
      writeln(a); // "now I'm a string"

  Examples:
  can also assign arrays

      alias Var = VariantN!(maxSize!(int[]));

      Var a = new int[42];
      writeln(a.length); // 42
      a[5] = 7;
      writeln(a[5]); // 7

  Examples:
  Can also assign class values

      alias Var = VariantN!(maxSize!(int*)); // classes are pointers
      Var a;

      class Foo {}
      auto foo = new Foo;
      a = foo;
      assert(*a.peek!(Foo) == foo); // and full type information is preserved

  alias `AllowedTypes` = This2Variant!(VariantN, AllowedTypesParam);
    The list of allowed types. If empty, any type is allowed.
  enum bool `allowed`(T);
    Tells whether a type T is statically allowed for storage inside a VariantN object by looking T up in AllowedTypes.
  this(T)(T `value`);
    Constructs a VariantN value given an argument of a generic type. Statically rejects disallowed types.
  this(T : VariantN!(tsize, Types), size_t tsize, Types...)(T `value`)
  if (!is(T : VariantN) && (Types.length > 0) && allSatisfy!(allowed, Types));
    Allows assignment from a subset algebraic type
  VariantN `opAssign`(T)(T `rhs`);
    Assigns a VariantN from a generic argument. Statically rejects disallowed types.
  const pure nothrow @property bool `hasValue`();
    Returns true if and only if the VariantN object holds a valid value (has been initialized with, or assigned from, a valid value).
    Examples:

        Variant a;
        assert(!a.hasValue);
        Variant b;
        a = b;
        assert(!a.hasValue); // still no value
        a = 5;
        assert(a.hasValue);

  inout @property inout(T)* `peek`(T)();
    If the VariantN object holds a value of the exact type T, returns a pointer to that value. Otherwise, returns null. In cases where T is statically disallowed, peek will not compile.
    Examples:

        Variant a = 5;
        auto b = a.peek!(int);
        assert(b !is null);
        *b = 6;
        writeln(a); // 6

  const nothrow @property @trusted TypeInfo `type`();
    Returns the typeid of the currently held value.
  const @property bool `convertsTo`(T)();
    Returns true if and only if the VariantN object holds an object implicitly convertible to type T. Implicit convertibility is defined as per AllImplicitConversionTargets.
  inout @property inout(T) `get`(T)();
  inout @property auto `get`(uint index)()
  if (index < AllowedTypes.length);
    Returns the value stored in the VariantN object, either by specifying the needed type or the index in the list of allowed types. The latter overload only applies to bounded variants (e.g. Algebraic).
    Parameters:

    T | The requested type. The currently stored value must implicitly convert to the requested type, in fact DecayStaticToDynamicArray!T. If an implicit conversion is not possible, throws a VariantException.
    index | The index of the type among AllowedTypesParam, zero-based.

  @property T `coerce`(T)();
    Returns the value stored in the VariantN object, explicitly converted (coerced) to the requested type T. If T is a string type, the value is formatted as a string. If the VariantN object is a string, a parse of the string to type T is attempted. If a conversion is not possible, throws a VariantException.
  string `toString`();
    Formats the stored value as a string.
  const bool `opEquals`(T)(auto ref T `rhs`)
  if (allowed!T || is(immutable(T) == immutable(VariantN)));
    Comparison for equality used by the "==" and "!=" operators.
  int `opCmp`(T)(T `rhs`)
  if (allowed!T);
    Ordering comparison used by the "<", "<=", ">", and ">=" operators. In case comparison is not sensible between the held value and `rhs`, an exception is thrown.
  const nothrow @safe size_t `toHash`();
    Computes the hash of the held value.
  VariantN `opBinary`(string op, T)(T `rhs`)
  if ((op == "+" || op == "-" || op == "*" || op == "/" || op == "^^" || op == "%") && is(typeof(opArithmetic!(T, op)(`rhs`))));
  VariantN `opBinary`(string op, T)(T `rhs`)
  if ((op == "&" || op == "|" || op == "^" || op == ">>" || op == "<<" || op == ">>>") && is(typeof(opLogic!(T, op)(`rhs`))));
  VariantN `opBinaryRight`(string op, T)(T `lhs`)
  if ((op == "+" || op == "*") && is(typeof(opArithmetic!(T, op)(`lhs`))));
  VariantN `opBinaryRight`(string op, T)(T `lhs`)
  if ((op == "&" || op == "|" || op == "^") && is(typeof(opLogic!(T, op)(`lhs`))));
  VariantN `opBinary`(string op, T)(T `rhs`)
  if (op == "~");
  VariantN `opOpAssign`(string op, T)(T `rhs`);
    Arithmetic between VariantN objects and numeric values. All arithmetic operations return a VariantN object typed depending on the types of both values involved. The conversion rules mimic D's built-in rules for arithmetic conversions.
  inout inout(Variant) `opIndex`(K)(K `i`);
  Variant `opIndexAssign`(T, N)(T `value`, N `i`);
  Variant `opIndexOpAssign`(string op, T, N)(T `value`, N `i`);
    Array and associative array operations. If a VariantN contains an (associative) array, it can be indexed into. Otherwise, an exception is thrown.
    Examples:

        Variant a = new int[10];
        a[5] = 42;
        writeln(a[5]); // 42
        a[5] += 8;
        writeln(a[5]); // 50

        int[int] hash = [ 42:24 ];
        a = hash;
        writeln(a[42]); // 24
        a[42] /= 2;
        writeln(a[42]); // 12

  @property size_t `length`();
    If the VariantN contains an (associative) array, returns the length of that array. Otherwise, throws an exception.
  int `opApply`(Delegate)(scope Delegate `dg`)
  if (is(Delegate == delegate));
    If the VariantN contains an array, applies `dg` to each element of the array in turn. Otherwise, throws an exception.
template `Algebraic`(T...)
  Algebraic data type restricted to a closed set of possible types. It's an alias for VariantN with an appropriately-constructed maximum size. `Algebraic` is useful when it is desirable to restrict what a discriminated type could hold to the end of defining simpler and more efficient manipulation.
  Warning: Algebraic is outdated and not recommended for use in new code. Instead, use std.sumtype.SumType.
  Examples:

      auto v = Algebraic!(int, double, string)(5);
      assert(v.peek!(int));
      v = 3.14;
      assert(v.peek!(double));
      // auto x = v.peek!(long); // won't compile, type long not allowed
      // v = '1'; // won't compile, type char not allowed

  Examples:

  #### Self-Referential Types

  A useful and popular use of algebraic data structures is for defining self-referential data structures, i.e. structures that embed references to values of their own type within.
  This is achieved with `Algebraic` by using This as a placeholder whenever a reference to the type being defined is needed. The `Algebraic` instantiation will perform alpha renaming on its constituent types, replacing This with the self-referenced type. The structure of the type involving This may be arbitrarily complex.

      import std.typecons : Tuple, tuple;

      // A tree is either a leaf or a branch of two other trees
      alias Tree(Leaf) = Algebraic!(Leaf, Tuple!(This*, This*));
      Tree!int tree = tuple(new Tree!int(42), new Tree!int(43));
      Tree!int* right = tree.get!1[1];
      writeln(*right); // 43

      // An object is a double, a string, or a hash of objects
      alias Obj = Algebraic!(double, string, This[string]);
      Obj obj = "hello";
      writeln(obj.get!1); // "hello"
      obj = 42.0;
      writeln(obj.get!0); // 42
      obj = ["customer": Obj("John"), "paid": Obj(23.95)];
      writeln(obj.get!2["customer"]); // "John"

alias `Variant` = VariantN!32LU.VariantN;
  Alias for VariantN instantiated with the largest size of creal, char[], and void delegate(). This ensures that `Variant` is large enough to hold all of D's predefined types unboxed, including all numeric types, pointers, delegates, and class references. You may want to use VariantN directly with a different maximum size either for storing larger types unboxed, or for saving memory.
  Examples:

      Variant a; // Must assign before use, otherwise exception ensues
      // Initialize with an integer; make the type int
      Variant b = 42;
      writeln(b.type); // typeid (int)
      // Peek at the value
      assert(b.peek!(int) !is null && *b.peek!(int) == 42);
      // Automatically convert per language rules
      auto x = b.get!(real);

      // Assign any other type, including other variants
      a = b;
      a = 3.14;
      writeln(a.type); // typeid (double)
      // Implicit conversions work just as with built-in types
      assert(a < b);
      // Check for convertibility
      assert(!a.convertsTo!(int)); // double not convertible to int
      // Strings and all other arrays are supported
      a = "now I'm a string";
      writeln(a); // "now I'm a string"

  Examples:
  can also assign arrays

      Variant a = new int[42];
      writeln(a.length); // 42
      a[5] = 7;
      writeln(a[5]); // 7

  Examples:
  Can also assign class values

      Variant a;

      class Foo {}
      auto foo = new Foo;
      a = foo;
      assert(*a.peek!(Foo) == foo); // and full type information is preserved

Variant[] `variantArray`(T...)(T `args`);
  Returns an array of variants constructed from `args`.
  This is by design. During construction the Variant needs static type information about the type being held, so as to store a pointer to function for fast retrieval.
  Examples:

      auto a = variantArray(1, 3.14, "Hi!");
      writeln(a[1]); // 3.14
      auto b = Variant(a); // variant array as variant
      writeln(b[1]); // 3.14

class `VariantException`: object.Exception;
  Thrown in three cases:
  1. An uninitialized Variant is used in any way except assignment and hasValue;
  2. A get or coerce is attempted with an incompatible target type;
  3. A comparison between Variant objects of incompatible types is attempted.
  Examples:

      import std.exception : assertThrown;

      Variant v;

      // uninitialized use
      assertThrown!VariantException(v + 1);
      assertThrown!VariantException(v.length);

      // .get with an incompatible target type
      assertThrown!VariantException(Variant("a").get!int);

      // comparison between incompatible types
      assertThrown!VariantException(Variant(3) < Variant("a"));

  TypeInfo `source`;
    The source type in the conversion or comparison
  TypeInfo `target`;
    The target type in the conversion or comparison
template `visit`(Handlers...) if (Handlers.length > 0)
  Applies a delegate or function to the given Algebraic depending on the held type, ensuring that all types are handled by the visiting functions.
  The delegate or function having the currently held value as parameter is called with variant's current value. Visiting handlers are passed in the template parameter list. It is statically ensured that all held types of variant are handled across all handlers. `visit` allows delegates and static functions to be passed as parameters.
  If a function with an untyped parameter is specified, this function is called when the variant contains a type that does not match any other function. This can be used to apply the same function across multiple possible types. Exactly one generic function is allowed.
  If a function without parameters is specified, this function is called when variant doesn't hold a value. Exactly one parameter-less function is allowed.
  Duplicate overloads matching the same type in one of the visitors are disallowed.
  Returns:
  The return type of visit is deduced from the visiting functions and must be the same across all overloads.
  Throws:
  VariantException if variant doesn't hold a value and no parameter-less fallback function is specified.
  Examples:

      Algebraic!(int, string) variant;

      variant = 10;
      assert(variant.visit!((string s) => cast(int) s.length,
                            (int i)    => i)()
                            == 10);
      variant = "string";
      assert(variant.visit!((int i) => i,
                            (string s) => cast(int) s.length)()
                            == 6);

      // Error function usage
      Algebraic!(int, string) emptyVar;
      auto rslt = emptyVar.visit!((string s) => cast(int) s.length,
                            (int i)    => i,
                            () => -1)();
      writeln(rslt); // -1

      // Generic function usage
      Algebraic!(int, float, real) number = 2;
      writeln(number.visit!(x => x += 1)); // 3

      // Generic function for int/float with separate behavior for string
      Algebraic!(int, float, string) something = 2;
      assert(something.visit!((string s) => s.length, x => x) == 2); // generic
      something = "asdf";
      assert(something.visit!((string s) => s.length, x => x) == 4); // string

      // Generic handler and empty handler
      Algebraic!(int, float, real) empty2;
      writeln(empty2.visit!(x => x + 1, () => -1)); // -1

  auto `visit`(VariantType)(VariantType `variant`)
  if (isAlgebraic!VariantType);
template `tryVisit`(Handlers...) if (Handlers.length > 0)
  Behaves as visit but doesn't enforce that all types are handled by the visiting functions.
  If a parameter-less function is specified it is called when either variant doesn't hold a value or holds a type which isn't handled by the visiting functions.
  Returns:
  The return type of tryVisit is deduced from the visiting functions and must be the same across all overloads.
  Throws:
  VariantException if variant doesn't hold a value or variant holds a value which isn't handled by the visiting functions, when no parameter-less fallback function is specified.
  Examples:

      Algebraic!(int, string) variant;

      variant = 10;
      auto which = -1;
      variant.tryVisit!((int i) { which = 0; })();
      writeln(which); // 0

      // Error function usage
      variant = "test";
      variant.tryVisit!((int i) { which = 0; },
                        ()      { which = -100; })();
      writeln(which); // -100

  auto `tryVisit`(VariantType)(VariantType `variant`)
  if (isAlgebraic!VariantType);

Copyright © 1999-2022 by the D Language Foundation | Page generated by Ddoc on Mon Nov 14 18:07:26 2022