diff --git a/subspace/choice/__private/storage.h b/subspace/choice/__private/storage.h
index 7517d5757..04e01f206 100644
--- a/subspace/choice/__private/storage.h
+++ b/subspace/choice/__private/storage.h
@@ -161,6 +161,22 @@ union Storage<I, ::sus::Tuple<Ts...>, Elements...> {
       return more_.partial_ord(index, other.more_);
     }
   }
+  inline constexpr void visit(size_t index, auto tag,
+                              auto&& overload_set) const& {
+    if (index == I) {
+      overload_set(tag, get_ref());
+    } else {
+      more_.visit(index, tag, ::sus::move(overload_set));
+    }
+  }
+  inline constexpr void visit_mut(size_t index, auto tag,
+                                  auto&& overload_set) & {
+    if (index == I) {
+      overload_set(tag, get_mut());
+    } else {
+      more_.visit_mut(index, tag, ::sus::move(overload_set));
+    }
+  }
 
   constexpr auto get_ref() const& {
     return [this]<size_t... Is>(std::index_sequence<Is...>) {
@@ -247,6 +263,22 @@ union Storage<I, Nothing, Elements...> {
       return more_.partial_ord(index, other.more_);
     }
   }
+  inline constexpr void visit(size_t index, auto tag,
+                              auto&& overload_set) const& {
+    if (index == I) {
+      overload_set(tag);
+    } else {
+      more_.visit(index, tag, ::sus::move(overload_set));
+    }
+  }
+  inline constexpr void visit_mut(size_t index, auto tag,
+                                  auto&& overload_set) & {
+    if (index == I) {
+      overload_set(tag);
+    } else {
+      more_.visit_mut(index, tag, ::sus::move(overload_set));
+    }
+  }
 
   [[sus_no_unique_address]] Storage<I + 1, Elements...> more_;
 };
@@ -342,6 +374,22 @@ union Storage<I, ::sus::Tuple<T>, Elements...> {
       return more_.partial_ord(index, other.more_);
     }
   }
+  inline constexpr void visit(size_t index, auto tag,
+                              auto&& overload_set) const& {
+    if (index == I) {
+      overload_set(tag, get_ref());
+    } else {
+      more_.visit(index, tag, ::sus::move(overload_set));
+    }
+  }
+  inline constexpr void visit_mut(size_t index, auto tag,
+                                  auto&& overload_set) & {
+    if (index == I) {
+      overload_set(tag, get_mut());
+    } else {
+      more_.visit_mut(index, tag, ::sus::move(overload_set));
+    }
+  }
 
   inline constexpr decltype(auto) get_ref() const& {
     return tuple_.template get_ref<0>();
@@ -412,6 +460,16 @@ union Storage<I, ::sus::Tuple<Ts...>> {
     ::sus::check(index == I);
     return std::partial_order(tuple_, other.tuple_);
   }
+  inline constexpr void visit(size_t index, auto tag,
+                              auto&& overload_set) const& {
+    ::sus::check(index == I);
+    overload_set(tag, get_ref());
+  }
+  inline constexpr void visit_mut(size_t index, auto tag,
+                                  auto&& overload_set) & {
+    ::sus::check(index == I);
+    overload_set(tag, get_mut());
+  }
 
   constexpr auto get_ref() const& {
     return [this]<size_t... Is>(std::index_sequence<Is...>) {
@@ -466,6 +524,16 @@ union Storage<I, Nothing> {
     ::sus::check(index == I);
     return std::partial_ordering::equivalent;
   }
+  inline constexpr void visit(size_t index, auto tag,
+                              auto&& overload_set) const& {
+    ::sus::check(index == I);
+    overload_set(tag);
+  }
+  inline constexpr void visit_mut(size_t index, auto tag,
+                                  auto&& overload_set) & {
+    ::sus::check(index == I);
+    overload_set(tag);
+  }
 };
 
 template <size_t I, class T>
@@ -523,6 +591,16 @@ union Storage<I, ::sus::Tuple<T>> {
     ::sus::check(index == I);
     return std::partial_order(tuple_, other.tuple_);
   }
+  inline constexpr void visit(size_t index, auto tag,
+                              auto&& overload_set) const& {
+    ::sus::check(index == I);
+    overload_set(tag, get_ref());
+  }
+  inline constexpr void visit_mut(size_t index, auto tag,
+                                  auto&& overload_set) & {
+    ::sus::check(index == I);
+    overload_set(tag, get_mut());
+  }
 
   inline constexpr decltype(auto) get_ref() const& {
     return tuple_.template get_ref<0>();
@@ -541,36 +619,39 @@ union Storage<I, ::sus::Tuple<T>> {
 
 template <auto I, class S>
 static constexpr const auto& find_choice_storage(const S& storage) {
-  return find_choice_storage(storage, std::integral_constant<size_t, size_t{I}>());
+  return find_choice_storage(storage,
+                             std::integral_constant<size_t, size_t{I}>());
 }
 
 template <size_t I, class S>
-static constexpr const auto& find_choice_storage(const S& storage,
-                                          std::integral_constant<size_t, I>) {
-  return find_choice_storage(storage.more_, std::integral_constant<size_t, I - 1u>());
+static constexpr const auto& find_choice_storage(
+    const S& storage, std::integral_constant<size_t, I>) {
+  return find_choice_storage(storage.more_,
+                             std::integral_constant<size_t, I - 1u>());
 }
 
 template <class S>
-static constexpr const auto& find_choice_storage(const S& storage,
-                                          std::integral_constant<size_t, 0>) {
+static constexpr const auto& find_choice_storage(
+    const S& storage, std::integral_constant<size_t, 0>) {
   return storage;
 }
 
 template <auto I, class S>
 static constexpr auto& find_choice_storage_mut(S& storage) {
-  return find_choice_storage_mut(storage, std::integral_constant<size_t, size_t{I}>());
+  return find_choice_storage_mut(storage,
+                                 std::integral_constant<size_t, size_t{I}>());
 }
 
 template <size_t I, class S>
-static constexpr auto& find_choice_storage_mut(S& storage,
-                                        std::integral_constant<size_t, I>) {
+static constexpr auto& find_choice_storage_mut(
+    S& storage, std::integral_constant<size_t, I>) {
   return find_choice_storage_mut(storage.more_,
-                          std::integral_constant<size_t, I - 1u>());
+                                 std::integral_constant<size_t, I - 1u>());
 }
 
 template <class S>
-static constexpr auto& find_choice_storage_mut(S& storage,
-                                        std::integral_constant<size_t, 0>) {
+static constexpr auto& find_choice_storage_mut(
+    S& storage, std::integral_constant<size_t, 0>) {
   return storage;
 }
 
diff --git a/subspace/choice/choice.h b/subspace/choice/choice.h
index cb49bb94c..4cc29d5d2 100644
--- a/subspace/choice/choice.h
+++ b/subspace/choice/choice.h
@@ -348,6 +348,91 @@ class Choice<__private::TypeList<Ts...>, Tags...> final {
     }
   }
 
+  /// Calls overload_set with a const reference to the currently stored value in
+  /// the Choice.
+  ///
+  /// Typically the `overload_set` would be object which has `operator()`
+  /// overloaded for each possible value type in the Choice. If there is no
+  /// `void` type attached to any tag, then a templated function can be used as
+  /// well.
+  ///
+  /// Because the same type may be stored for multiple tag values, the tag
+  /// value is also passed, as the first parameter, to the `overload_set`.
+  ///
+  /// If the type attached to a tag is void, the overload for that tag would
+  /// only receive the tag as a parameter: `fn(Tag t)`. Otherwise the
+  /// overload will receive the tag and the value as parameters:
+  /// `fn(Tag t, const TagValue& v)`.
+  ///
+  /// # Example
+  ///
+  /// ```cpp
+  /// struct Visitor {
+  ///   void operator()(Order which, const u32& v) {
+  ///     printf("First\n");
+  ///   }
+  ///   void operator()(Order which, const i32& v) {
+  ///     printf("Second\n");
+  ///   }
+  ///   void operator()(Order which /* void */) {
+  ///     printf("Third\n");
+  ///   }
+  /// };
+  /// Visitor visitor;
+  ///
+  /// auto c = Choice<sus_choice_types(
+  ///     (Order::First, u32),
+  ///     (Order::Second, i32))
+  ///   >::with<Order::First>(4u);
+  /// c.visit(visitor);  // Prints "First".
+  /// ```
+  void visit(auto&& overload_set) const& {
+    storage_.visit(size_t{index_}, which(), ::sus::move(overload_set));
+  }
+  void visit(auto&& overload_set) && = delete;
+
+  /// Calls overload_set with a mutable reference to the currently stored value
+  /// in the Choice.
+  ///
+  /// Typically the `overload_set` would be object which has `operator()`
+  /// overloaded for each possible value type in the Choice. If there is no
+  /// `void` type attached to any tag, then a templated function can be used as
+  /// well.
+  ///
+  /// Because the same type may be stored for multiple tag values, the tag
+  /// value is also passed, as the first parameter, to the `overload_set`.
+  ///
+  /// If the type attached to a tag is void, the overload for that tag would
+  /// only receive the tag as a parameter: `fn(Tag t)`. Otherwise the
+  /// overload will receive the tag and the value as parameters:
+  /// `fn(Tag t, TagValue& v)`.
+  ///
+  /// # Example
+  ///
+  /// ```cpp
+  /// struct Visitor {
+  ///   void operator()(Order which, u32& v) {
+  ///     printf("First\n");
+  ///   }
+  ///   void operator()(Order which, i32& v) {
+  ///     printf("Second\n");
+  ///   }
+  ///   void operator()(Order which /* void */) {
+  ///     printf("Third\n");
+  ///   }
+  /// };
+  /// Visitor visitor;
+  ///
+  /// auto c = Choice<sus_choice_types(
+  ///     (Order::First, u32),
+  ///     (Order::Second, i32))
+  ///   >::with<Order::First>(4u);
+  /// c.visit_mut(visitor);  // Prints "First".
+  /// ```
+  void visit_mut(auto&& overload_set) & {
+    storage_.visit_mut(size_t{index_}, which(), ::sus::move(overload_set));
+  }
+
   template <TagsType V, int&...,
             __private::ValueIsVoid Arg = StorageTypeOfTag<V>>
   void set() & noexcept {
diff --git a/subspace/choice/choice_unittest.cc b/subspace/choice/choice_unittest.cc
index a528e294e..41e4496bd 100644
--- a/subspace/choice/choice_unittest.cc
+++ b/subspace/choice/choice_unittest.cc
@@ -535,4 +535,40 @@ TEST(Choice, VoidValues) {
   EXPECT_LT(u4, u6);
 }
 
+TEST(Choice, Visit) {
+  struct Visitor {
+    void operator()(Order which, const u32& v) {
+      EXPECT_EQ(which, Order::First);
+      EXPECT_EQ(v, 4u);
+      called.insert(which);
+    }
+    void operator()(Order which, const i32& v) {
+      EXPECT_EQ(which, Order::Second);
+      EXPECT_EQ(v, 2);
+      called.insert(which);
+    }
+    void operator()(Order which) {
+      EXPECT_EQ(which, Order::Third);
+      called.insert(which);
+    }
+
+    sus::Option<Order> called;
+  };
+  Visitor visitor;
+
+  auto c =
+      Choice<sus_choice_types((Order::First, u32), (Order::Second, i32),
+                              (Order::Third, void))>::with<Order::First>(4u);
+  c.visit(visitor);
+  EXPECT_EQ(visitor.called.take().unwrap(), Order::First);
+
+  c.set<Order::Second>(2);
+  c.visit(visitor);
+  EXPECT_EQ(visitor.called.take().unwrap(), Order::Second);
+
+  c.set<Order::Third>();
+  c.visit(visitor);
+  EXPECT_EQ(visitor.called.take().unwrap(), Order::Third);
+}
+
 }  // namespace