Decouple Context from the Descriptor

PiperOrigin-RevId: 592029759
diff --git a/src/google/protobuf/compiler/rust/accessors/accessor_generator.h b/src/google/protobuf/compiler/rust/accessors/accessor_generator.h
index 442de3c..6aad7bb 100644
--- a/src/google/protobuf/compiler/rust/accessors/accessor_generator.h
+++ b/src/google/protobuf/compiler/rust/accessors/accessor_generator.h
@@ -26,25 +26,26 @@
   AccessorGenerator() = default;
   virtual ~AccessorGenerator() = default;
 
-  AccessorGenerator(const AccessorGenerator &) = delete;
-  AccessorGenerator(AccessorGenerator &&) = delete;
-  AccessorGenerator &operator=(const AccessorGenerator &) = delete;
-  AccessorGenerator &operator=(AccessorGenerator &&) = delete;
+  AccessorGenerator(const AccessorGenerator&) = delete;
+  AccessorGenerator(AccessorGenerator&&) = delete;
+  AccessorGenerator& operator=(const AccessorGenerator&) = delete;
+  AccessorGenerator& operator=(AccessorGenerator&&) = delete;
 
   // Constructs a generator for the given field.
   //
   // Returns `nullptr` if there is no known generator for this field.
-  static std::unique_ptr<AccessorGenerator> For(Context<FieldDescriptor> field);
+  static std::unique_ptr<AccessorGenerator> For(Context& ctx,
+                                                const FieldDescriptor& field);
 
-  void GenerateMsgImpl(Context<FieldDescriptor> field) const {
-    InMsgImpl(field);
+  void GenerateMsgImpl(Context& ctx, const FieldDescriptor& field) const {
+    InMsgImpl(ctx, field);
   }
-  void GenerateExternC(Context<FieldDescriptor> field) const {
-    InExternC(field);
+  void GenerateExternC(Context& ctx, const FieldDescriptor& field) const {
+    InExternC(ctx, field);
   }
-  void GenerateThunkCc(Context<FieldDescriptor> field) const {
-    ABSL_CHECK(field.is_cpp());
-    InThunkCc(field);
+  void GenerateThunkCc(Context& ctx, const FieldDescriptor& field) const {
+    ABSL_CHECK(ctx.is_cpp());
+    InThunkCc(ctx, field);
   }
 
  private:
@@ -54,53 +55,53 @@
   // prologue to inject variables automatically.
 
   // Called inside the main inherent `impl Msg {}` block.
-  virtual void InMsgImpl(Context<FieldDescriptor> field) const {}
+  virtual void InMsgImpl(Context& ctx, const FieldDescriptor& field) const {}
 
   // Called inside of a message's `extern "C" {}` block.
-  virtual void InExternC(Context<FieldDescriptor> field) const {}
+  virtual void InExternC(Context& ctx, const FieldDescriptor& field) const {}
 
   // Called inside of an `extern "C" {}` block in the  `.thunk.cc` file, if such
   // a file is being generated.
-  virtual void InThunkCc(Context<FieldDescriptor> field) const {}
+  virtual void InThunkCc(Context& ctx, const FieldDescriptor& field) const {}
 };
 
 class SingularScalar final : public AccessorGenerator {
  public:
   ~SingularScalar() override = default;
-  void InMsgImpl(Context<FieldDescriptor> field) const override;
-  void InExternC(Context<FieldDescriptor> field) const override;
-  void InThunkCc(Context<FieldDescriptor> field) const override;
+  void InMsgImpl(Context& ctx, const FieldDescriptor& field) const override;
+  void InExternC(Context& ctx, const FieldDescriptor& field) const override;
+  void InThunkCc(Context& ctx, const FieldDescriptor& field) const override;
 };
 
 class SingularString final : public AccessorGenerator {
  public:
   ~SingularString() override = default;
-  void InMsgImpl(Context<FieldDescriptor> field) const override;
-  void InExternC(Context<FieldDescriptor> field) const override;
-  void InThunkCc(Context<FieldDescriptor> field) const override;
+  void InMsgImpl(Context& ctx, const FieldDescriptor& field) const override;
+  void InExternC(Context& ctx, const FieldDescriptor& field) const override;
+  void InThunkCc(Context& ctx, const FieldDescriptor& field) const override;
 };
 
 class SingularMessage final : public AccessorGenerator {
  public:
   ~SingularMessage() override = default;
-  void InMsgImpl(Context<FieldDescriptor> field) const override;
-  void InExternC(Context<FieldDescriptor> field) const override;
-  void InThunkCc(Context<FieldDescriptor> field) const override;
+  void InMsgImpl(Context& ctx, const FieldDescriptor& field) const override;
+  void InExternC(Context& ctx, const FieldDescriptor& field) const override;
+  void InThunkCc(Context& ctx, const FieldDescriptor& field) const override;
 };
 
 class RepeatedScalar final : public AccessorGenerator {
  public:
   ~RepeatedScalar() override = default;
-  void InMsgImpl(Context<FieldDescriptor> field) const override;
-  void InExternC(Context<FieldDescriptor> field) const override;
-  void InThunkCc(Context<FieldDescriptor> field) const override;
+  void InMsgImpl(Context& ctx, const FieldDescriptor& field) const override;
+  void InExternC(Context& ctx, const FieldDescriptor& field) const override;
+  void InThunkCc(Context& ctx, const FieldDescriptor& field) const override;
 };
 
 class UnsupportedField final : public AccessorGenerator {
  public:
   explicit UnsupportedField(std::string reason) : reason_(std::move(reason)) {}
   ~UnsupportedField() override = default;
-  void InMsgImpl(Context<FieldDescriptor> field) const override;
+  void InMsgImpl(Context& ctx, const FieldDescriptor& field) const override;
 
  private:
   std::string reason_;
@@ -109,9 +110,9 @@
 class Map final : public AccessorGenerator {
  public:
   ~Map() override = default;
-  void InMsgImpl(Context<FieldDescriptor> field) const override;
-  void InExternC(Context<FieldDescriptor> field) const override;
-  void InThunkCc(Context<FieldDescriptor> field) const override;
+  void InMsgImpl(Context& ctx, const FieldDescriptor& field) const override;
+  void InExternC(Context& ctx, const FieldDescriptor& field) const override;
+  void InThunkCc(Context& ctx, const FieldDescriptor& field) const override;
 };
 
 }  // namespace rust
diff --git a/src/google/protobuf/compiler/rust/accessors/accessors.cc b/src/google/protobuf/compiler/rust/accessors/accessors.cc
index c25fbc2..d4c93f0 100644
--- a/src/google/protobuf/compiler/rust/accessors/accessors.cc
+++ b/src/google/protobuf/compiler/rust/accessors/accessors.cc
@@ -23,17 +23,16 @@
 namespace {
 
 std::unique_ptr<AccessorGenerator> AccessorGeneratorFor(
-    Context<FieldDescriptor> field) {
-  const FieldDescriptor& desc = field.desc();
+    Context& ctx, const FieldDescriptor& field) {
   // TODO: We do not support [ctype=FOO] (used to set the field
   // type in C++ to cord or string_piece) in V0.6 API.
-  if (desc.options().has_ctype()) {
+  if (field.options().has_ctype()) {
     return std::make_unique<UnsupportedField>(
         "fields with ctype not supported");
   }
 
-  if (desc.is_map()) {
-    auto value_type = desc.message_type()->map_value()->type();
+  if (field.is_map()) {
+    auto value_type = field.message_type()->map_value()->type();
     switch (value_type) {
       case FieldDescriptor::TYPE_BYTES:
       case FieldDescriptor::TYPE_ENUM:
@@ -46,7 +45,7 @@
     }
   }
 
-  switch (desc.type()) {
+  switch (field.type()) {
     case FieldDescriptor::TYPE_INT32:
     case FieldDescriptor::TYPE_INT64:
     case FieldDescriptor::TYPE_FIXED32:
@@ -60,22 +59,22 @@
     case FieldDescriptor::TYPE_FLOAT:
     case FieldDescriptor::TYPE_DOUBLE:
     case FieldDescriptor::TYPE_BOOL:
-      if (desc.is_repeated()) {
+      if (field.is_repeated()) {
         return std::make_unique<RepeatedScalar>();
       }
       return std::make_unique<SingularScalar>();
     case FieldDescriptor::TYPE_BYTES:
     case FieldDescriptor::TYPE_STRING:
-      if (desc.is_repeated()) {
+      if (field.is_repeated()) {
         return std::make_unique<UnsupportedField>("repeated str not supported");
       }
       return std::make_unique<SingularString>();
     case FieldDescriptor::TYPE_MESSAGE:
-      if (desc.is_repeated()) {
+      if (field.is_repeated()) {
         return std::make_unique<UnsupportedField>("repeated msg not supported");
       }
-      if (!field.generator_context().is_file_in_current_crate(
-              desc.message_type()->file())) {
+      if (!ctx.generator_context().is_file_in_current_crate(
+              *field.message_type()->file())) {
         return std::make_unique<UnsupportedField>(
             "message fields that are imported from another proto_library"
             " (defined in a separate Rust crate) are not supported");
@@ -89,21 +88,21 @@
       return std::make_unique<UnsupportedField>("group not supported");
   }
 
-  ABSL_LOG(FATAL) << "Unexpected field type: " << desc.type();
+  ABSL_LOG(FATAL) << "Unexpected field type: " << field.type();
 }
 
 }  // namespace
 
-void GenerateAccessorMsgImpl(Context<FieldDescriptor> field) {
-  AccessorGeneratorFor(field)->GenerateMsgImpl(field);
+void GenerateAccessorMsgImpl(Context& ctx, const FieldDescriptor& field) {
+  AccessorGeneratorFor(ctx, field)->GenerateMsgImpl(ctx, field);
 }
 
-void GenerateAccessorExternC(Context<FieldDescriptor> field) {
-  AccessorGeneratorFor(field)->GenerateExternC(field);
+void GenerateAccessorExternC(Context& ctx, const FieldDescriptor& field) {
+  AccessorGeneratorFor(ctx, field)->GenerateExternC(ctx, field);
 }
 
-void GenerateAccessorThunkCc(Context<FieldDescriptor> field) {
-  AccessorGeneratorFor(field)->GenerateThunkCc(field);
+void GenerateAccessorThunkCc(Context& ctx, const FieldDescriptor& field) {
+  AccessorGeneratorFor(ctx, field)->GenerateThunkCc(ctx, field);
 }
 
 }  // namespace rust
diff --git a/src/google/protobuf/compiler/rust/accessors/accessors.h b/src/google/protobuf/compiler/rust/accessors/accessors.h
index 05687e5..801bff2 100644
--- a/src/google/protobuf/compiler/rust/accessors/accessors.h
+++ b/src/google/protobuf/compiler/rust/accessors/accessors.h
@@ -16,9 +16,9 @@
 namespace compiler {
 namespace rust {
 
-void GenerateAccessorMsgImpl(Context<FieldDescriptor> field);
-void GenerateAccessorExternC(Context<FieldDescriptor> field);
-void GenerateAccessorThunkCc(Context<FieldDescriptor> field);
+void GenerateAccessorMsgImpl(Context& ctx, const FieldDescriptor& field);
+void GenerateAccessorExternC(Context& ctx, const FieldDescriptor& field);
+void GenerateAccessorThunkCc(Context& ctx, const FieldDescriptor& field);
 
 }  // namespace rust
 }  // namespace compiler
diff --git a/src/google/protobuf/compiler/rust/accessors/helpers.cc b/src/google/protobuf/compiler/rust/accessors/helpers.cc
index b2d2a1b..ba41abd 100644
--- a/src/google/protobuf/compiler/rust/accessors/helpers.cc
+++ b/src/google/protobuf/compiler/rust/accessors/helpers.cc
@@ -15,7 +15,6 @@
 #include "absl/strings/escaping.h"
 #include "absl/strings/str_cat.h"
 #include "absl/strings/str_format.h"
-#include "google/protobuf/compiler/rust/context.h"
 #include "google/protobuf/descriptor.h"
 #include "google/protobuf/io/strtod.h"
 
@@ -24,33 +23,32 @@
 namespace compiler {
 namespace rust {
 
-std::string DefaultValue(Context<FieldDescriptor> field) {
-  switch (field.desc().type()) {
+std::string DefaultValue(const FieldDescriptor& field) {
+  switch (field.type()) {
     case FieldDescriptor::TYPE_DOUBLE:
-      if (std::isfinite(field.desc().default_value_double())) {
-        return absl::StrCat(io::SimpleDtoa(field.desc().default_value_double()),
+      if (std::isfinite(field.default_value_double())) {
+        return absl::StrCat(io::SimpleDtoa(field.default_value_double()),
                             "f64");
-      } else if (std::isnan(field.desc().default_value_double())) {
+      } else if (std::isnan(field.default_value_double())) {
         return std::string("f64::NAN");
-      } else if (field.desc().default_value_double() ==
+      } else if (field.default_value_double() ==
                  std::numeric_limits<double>::infinity()) {
         return std::string("f64::INFINITY");
-      } else if (field.desc().default_value_double() ==
+      } else if (field.default_value_double() ==
                  -std::numeric_limits<double>::infinity()) {
         return std::string("f64::NEG_INFINITY");
       } else {
         ABSL_LOG(FATAL) << "unreachable";
       }
     case FieldDescriptor::TYPE_FLOAT:
-      if (std::isfinite(field.desc().default_value_float())) {
-        return absl::StrCat(io::SimpleFtoa(field.desc().default_value_float()),
-                            "f32");
-      } else if (std::isnan(field.desc().default_value_float())) {
+      if (std::isfinite(field.default_value_float())) {
+        return absl::StrCat(io::SimpleFtoa(field.default_value_float()), "f32");
+      } else if (std::isnan(field.default_value_float())) {
         return std::string("f32::NAN");
-      } else if (field.desc().default_value_float() ==
+      } else if (field.default_value_float() ==
                  std::numeric_limits<float>::infinity()) {
         return std::string("f32::INFINITY");
-      } else if (field.desc().default_value_float() ==
+      } else if (field.default_value_float() ==
                  -std::numeric_limits<float>::infinity()) {
         return std::string("f32::NEG_INFINITY");
       } else {
@@ -59,27 +57,27 @@
     case FieldDescriptor::TYPE_INT32:
     case FieldDescriptor::TYPE_SFIXED32:
     case FieldDescriptor::TYPE_SINT32:
-      return absl::StrFormat("%d", field.desc().default_value_int32());
+      return absl::StrFormat("%d", field.default_value_int32());
     case FieldDescriptor::TYPE_INT64:
     case FieldDescriptor::TYPE_SFIXED64:
     case FieldDescriptor::TYPE_SINT64:
-      return absl::StrFormat("%d", field.desc().default_value_int64());
+      return absl::StrFormat("%d", field.default_value_int64());
     case FieldDescriptor::TYPE_FIXED64:
     case FieldDescriptor::TYPE_UINT64:
-      return absl::StrFormat("%u", field.desc().default_value_uint64());
+      return absl::StrFormat("%u", field.default_value_uint64());
     case FieldDescriptor::TYPE_FIXED32:
     case FieldDescriptor::TYPE_UINT32:
-      return absl::StrFormat("%u", field.desc().default_value_uint32());
+      return absl::StrFormat("%u", field.default_value_uint32());
     case FieldDescriptor::TYPE_BOOL:
-      return absl::StrFormat("%v", field.desc().default_value_bool());
+      return absl::StrFormat("%v", field.default_value_bool());
     case FieldDescriptor::TYPE_STRING:
     case FieldDescriptor::TYPE_BYTES:
-      return absl::StrFormat(
-          "b\"%s\"", absl::CHexEscape(field.desc().default_value_string()));
+      return absl::StrFormat("b\"%s\"",
+                             absl::CHexEscape(field.default_value_string()));
     case FieldDescriptor::TYPE_GROUP:
     case FieldDescriptor::TYPE_MESSAGE:
     case FieldDescriptor::TYPE_ENUM:
-      ABSL_LOG(FATAL) << "Unsupported field type: " << field.desc().type_name();
+      ABSL_LOG(FATAL) << "Unsupported field type: " << field.type_name();
   }
   ABSL_LOG(FATAL) << "unreachable";
 }
diff --git a/src/google/protobuf/compiler/rust/accessors/helpers.h b/src/google/protobuf/compiler/rust/accessors/helpers.h
index 45c7f3a..ee2c429 100644
--- a/src/google/protobuf/compiler/rust/accessors/helpers.h
+++ b/src/google/protobuf/compiler/rust/accessors/helpers.h
@@ -10,7 +10,6 @@
 
 #include <string>
 
-#include "google/protobuf/compiler/rust/context.h"
 #include "google/protobuf/descriptor.h"
 
 namespace google {
@@ -23,7 +22,7 @@
 // Both strings and bytes are represented as a byte string literal, i.e. in the
 // format `b"default value here"`. It is the caller's responsibility to convert
 // the byte literal to an actual string, if needed.
-std::string DefaultValue(Context<FieldDescriptor> field);
+std::string DefaultValue(const FieldDescriptor& field);
 
 }  // namespace rust
 }  // namespace compiler
diff --git a/src/google/protobuf/compiler/rust/accessors/map.cc b/src/google/protobuf/compiler/rust/accessors/map.cc
index 0c2c1eb..89535d4 100644
--- a/src/google/protobuf/compiler/rust/accessors/map.cc
+++ b/src/google/protobuf/compiler/rust/accessors/map.cc
@@ -17,19 +17,19 @@
 namespace compiler {
 namespace rust {
 
-void Map::InMsgImpl(Context<FieldDescriptor> field) const {
-  auto& key_type = *field.desc().message_type()->map_key();
-  auto& value_type = *field.desc().message_type()->map_value();
+void Map::InMsgImpl(Context& ctx, const FieldDescriptor& field) const {
+  auto& key_type = *field.message_type()->map_key();
+  auto& value_type = *field.message_type()->map_value();
 
-  field.Emit({{"field", field.desc().name()},
-              {"Key", PrimitiveRsTypeName(key_type)},
-              {"Value", PrimitiveRsTypeName(value_type)},
-              {"getter_thunk", Thunk(field, "get")},
-              {"getter_mut_thunk", Thunk(field, "get_mut")},
-              {"getter",
-               [&] {
-                 if (field.is_upb()) {
-                   field.Emit({}, R"rs(
+  ctx.Emit({{"field", field.name()},
+            {"Key", PrimitiveRsTypeName(key_type)},
+            {"Value", PrimitiveRsTypeName(value_type)},
+            {"getter_thunk", Thunk(ctx, field, "get")},
+            {"getter_mut_thunk", Thunk(ctx, field, "get_mut")},
+            {"getter",
+             [&] {
+               if (ctx.is_upb()) {
+                 ctx.Emit({}, R"rs(
                     pub fn r#$field$(&self)
                       -> $pb$::View<'_, $pb$::Map<$Key$, $Value$>> {
                       let inner = unsafe {
@@ -44,8 +44,8 @@
                       });
                       $pb$::MapView::from_inner($pbi$::Private, inner)
                     })rs");
-                 } else {
-                   field.Emit({}, R"rs(
+               } else {
+                 ctx.Emit({}, R"rs(
                     pub fn r#$field$(&self)
                       -> $pb$::View<'_, $pb$::Map<$Key$, $Value$>> {
                       let inner = $pbr$::MapInner {
@@ -55,12 +55,12 @@
                       };
                       $pb$::MapView::from_inner($pbi$::Private, inner)
                     })rs");
-                 }
-               }},
-              {"getter_mut",
-               [&] {
-                 if (field.is_upb()) {
-                   field.Emit({}, R"rs(
+               }
+             }},
+            {"getter_mut",
+             [&] {
+               if (ctx.is_upb()) {
+                 ctx.Emit({}, R"rs(
                     pub fn r#$field$_mut(&mut self)
                       -> $pb$::Mut<'_, $pb$::Map<$Key$, $Value$>> {
                       let raw = unsafe {
@@ -75,8 +75,8 @@
                       };
                       $pb$::MapMut::from_inner($pbi$::Private, inner)
                     })rs");
-                 } else {
-                   field.Emit({}, R"rs(
+               } else {
+                 ctx.Emit({}, R"rs(
                     pub fn r#$field$_mut(&mut self)
                       -> $pb$::Mut<'_, $pb$::Map<$Key$, $Value$>> {
                       let inner = $pbr$::MapInner {
@@ -86,30 +86,30 @@
                       };
                       $pb$::MapMut::from_inner($pbi$::Private, inner)
                     })rs");
-                 }
-               }}},
-             R"rs(
+               }
+             }}},
+           R"rs(
     $getter$
     $getter_mut$
     )rs");
 }
 
-void Map::InExternC(Context<FieldDescriptor> field) const {
-  field.Emit(
+void Map::InExternC(Context& ctx, const FieldDescriptor& field) const {
+  ctx.Emit(
       {
-          {"getter_thunk", Thunk(field, "get")},
-          {"getter_mut_thunk", Thunk(field, "get_mut")},
+          {"getter_thunk", Thunk(ctx, field, "get")},
+          {"getter_mut_thunk", Thunk(ctx, field, "get_mut")},
           {"getter",
            [&] {
-             if (field.is_upb()) {
-               field.Emit({}, R"rs(
+             if (ctx.is_upb()) {
+               ctx.Emit({}, R"rs(
                 fn $getter_thunk$(raw_msg: $pbi$::RawMessage)
                   -> Option<$pbi$::RawMap>;
                 fn $getter_mut_thunk$(raw_msg: $pbi$::RawMessage,
                   arena: $pbi$::RawArena) -> $pbi$::RawMap;
               )rs");
              } else {
-               field.Emit({}, R"rs(
+               ctx.Emit({}, R"rs(
                 fn $getter_thunk$(msg: $pbi$::RawMessage) -> $pbi$::RawMap;
                 fn $getter_mut_thunk$(msg: $pbi$::RawMessage,) -> $pbi$::RawMap;
               )rs");
@@ -121,20 +121,19 @@
   )rs");
 }
 
-void Map::InThunkCc(Context<FieldDescriptor> field) const {
-  field.Emit(
-      {{"field", cpp::FieldName(&field.desc())},
-       {"Key", cpp::PrimitiveTypeName(
-                   field.desc().message_type()->map_key()->cpp_type())},
-       {"Value", cpp::PrimitiveTypeName(
-                     field.desc().message_type()->map_value()->cpp_type())},
-       {"QualifiedMsg",
-        cpp::QualifiedClassName(field.desc().containing_type())},
-       {"getter_thunk", Thunk(field, "get")},
-       {"getter_mut_thunk", Thunk(field, "get_mut")},
+void Map::InThunkCc(Context& ctx, const FieldDescriptor& field) const {
+  ctx.Emit(
+      {{"field", cpp::FieldName(&field)},
+       {"Key",
+        cpp::PrimitiveTypeName(field.message_type()->map_key()->cpp_type())},
+       {"Value",
+        cpp::PrimitiveTypeName(field.message_type()->map_value()->cpp_type())},
+       {"QualifiedMsg", cpp::QualifiedClassName(field.containing_type())},
+       {"getter_thunk", Thunk(ctx, field, "get")},
+       {"getter_mut_thunk", Thunk(ctx, field, "get_mut")},
        {"impls",
         [&] {
-          field.Emit(
+          ctx.Emit(
               R"cc(
                 const void* $getter_thunk$($QualifiedMsg$& msg) {
                   return &msg.$field$();
diff --git a/src/google/protobuf/compiler/rust/accessors/repeated_scalar.cc b/src/google/protobuf/compiler/rust/accessors/repeated_scalar.cc
index 72f1d79..e0e1c6c 100644
--- a/src/google/protobuf/compiler/rust/accessors/repeated_scalar.cc
+++ b/src/google/protobuf/compiler/rust/accessors/repeated_scalar.cc
@@ -17,15 +17,16 @@
 namespace compiler {
 namespace rust {
 
-void RepeatedScalar::InMsgImpl(Context<FieldDescriptor> field) const {
-  field.Emit({{"field", field.desc().name()},
-              {"Scalar", PrimitiveRsTypeName(field.desc())},
-              {"getter_thunk", Thunk(field, "get")},
-              {"getter_mut_thunk", Thunk(field, "get_mut")},
-              {"getter",
-               [&] {
-                 if (field.is_upb()) {
-                   field.Emit({}, R"rs(
+void RepeatedScalar::InMsgImpl(Context& ctx,
+                               const FieldDescriptor& field) const {
+  ctx.Emit({{"field", field.name()},
+            {"Scalar", PrimitiveRsTypeName(field)},
+            {"getter_thunk", Thunk(ctx, field, "get")},
+            {"getter_mut_thunk", Thunk(ctx, field, "get_mut")},
+            {"getter",
+             [&] {
+               if (ctx.is_upb()) {
+                 ctx.Emit({}, R"rs(
                     pub fn r#$field$(&self) -> $pb$::RepeatedView<'_, $Scalar$> {
                       unsafe {
                         $getter_thunk$(
@@ -40,8 +41,8 @@
                         )
                     }
                   )rs");
-                 } else {
-                   field.Emit({}, R"rs(
+               } else {
+                 ctx.Emit({}, R"rs(
                     pub fn r#$field$(&self) -> $pb$::RepeatedView<'_, $Scalar$> {
                       unsafe {
                         $pb$::RepeatedView::from_raw(
@@ -51,13 +52,13 @@
                       }
                     }
                   )rs");
-                 }
-               }},
-              {"clearer_thunk", Thunk(field, "clear")},
-              {"field_mutator_getter",
-               [&] {
-                 if (field.is_upb()) {
-                   field.Emit({}, R"rs(
+               }
+             }},
+            {"clearer_thunk", Thunk(ctx, field, "clear")},
+            {"field_mutator_getter",
+             [&] {
+               if (ctx.is_upb()) {
+                 ctx.Emit({}, R"rs(
                     pub fn r#$field$_mut(&mut self) -> $pb$::RepeatedMut<'_, $Scalar$> {
                       unsafe {
                         $pb$::RepeatedMut::from_inner(
@@ -75,8 +76,8 @@
                       }
                     }
                   )rs");
-                 } else {
-                   field.Emit({}, R"rs(
+               } else {
+                 ctx.Emit({}, R"rs(
                       pub fn r#$field$_mut(&mut self) -> $pb$::RepeatedMut<'_, $Scalar$> {
                         unsafe {
                           $pb$::RepeatedMut::from_inner(
@@ -89,22 +90,23 @@
                         }
                       }
                     )rs");
-                 }
-               }}},
-             R"rs(
+               }
+             }}},
+           R"rs(
           $getter$
           $field_mutator_getter$
         )rs");
 }
 
-void RepeatedScalar::InExternC(Context<FieldDescriptor> field) const {
-  field.Emit({{"Scalar", PrimitiveRsTypeName(field.desc())},
-              {"getter_thunk", Thunk(field, "get")},
-              {"getter_mut_thunk", Thunk(field, "get_mut")},
-              {"getter",
-               [&] {
-                 if (field.is_upb()) {
-                   field.Emit(R"rs(
+void RepeatedScalar::InExternC(Context& ctx,
+                               const FieldDescriptor& field) const {
+  ctx.Emit({{"Scalar", PrimitiveRsTypeName(field)},
+            {"getter_thunk", Thunk(ctx, field, "get")},
+            {"getter_mut_thunk", Thunk(ctx, field, "get_mut")},
+            {"getter",
+             [&] {
+               if (ctx.is_upb()) {
+                 ctx.Emit(R"rs(
                     fn $getter_mut_thunk$(
                       raw_msg: $pbi$::RawMessage,
                       size: *const usize,
@@ -116,44 +118,44 @@
                       size: *const usize,
                     ) -> Option<$pbi$::RawRepeatedField>;
                   )rs");
-                 } else {
-                   field.Emit(R"rs(
+               } else {
+                 ctx.Emit(R"rs(
                     fn $getter_mut_thunk$(raw_msg: $pbi$::RawMessage) -> $pbi$::RawRepeatedField;
                     fn $getter_thunk$(raw_msg: $pbi$::RawMessage) -> $pbi$::RawRepeatedField;
                   )rs");
-                 }
-               }},
-              {"clearer_thunk", Thunk(field, "clear")}},
-             R"rs(
+               }
+             }},
+            {"clearer_thunk", Thunk(ctx, field, "clear")}},
+           R"rs(
           fn $clearer_thunk$(raw_msg: $pbi$::RawMessage);
           $getter$
         )rs");
 }
 
-void RepeatedScalar::InThunkCc(Context<FieldDescriptor> field) const {
-  field.Emit({{"field", cpp::FieldName(&field.desc())},
-              {"Scalar", cpp::PrimitiveTypeName(field.desc().cpp_type())},
-              {"QualifiedMsg",
-               cpp::QualifiedClassName(field.desc().containing_type())},
-              {"clearer_thunk", Thunk(field, "clear")},
-              {"getter_thunk", Thunk(field, "get")},
-              {"getter_mut_thunk", Thunk(field, "get_mut")},
-              {"impls",
-               [&] {
-                 field.Emit(
-                     R"cc(
-                       void $clearer_thunk$($QualifiedMsg$* msg) {
-                         msg->clear_$field$();
-                       }
-                       google::protobuf::RepeatedField<$Scalar$>* $getter_mut_thunk$($QualifiedMsg$* msg) {
-                         return msg->mutable_$field$();
-                       }
-                       const google::protobuf::RepeatedField<$Scalar$>& $getter_thunk$($QualifiedMsg$& msg) {
-                         return msg.$field$();
-                       }
-                     )cc");
-               }}},
-             "$impls$");
+void RepeatedScalar::InThunkCc(Context& ctx,
+                               const FieldDescriptor& field) const {
+  ctx.Emit({{"field", cpp::FieldName(&field)},
+            {"Scalar", cpp::PrimitiveTypeName(field.cpp_type())},
+            {"QualifiedMsg", cpp::QualifiedClassName(field.containing_type())},
+            {"clearer_thunk", Thunk(ctx, field, "clear")},
+            {"getter_thunk", Thunk(ctx, field, "get")},
+            {"getter_mut_thunk", Thunk(ctx, field, "get_mut")},
+            {"impls",
+             [&] {
+               ctx.Emit(
+                   R"cc(
+                     void $clearer_thunk$($QualifiedMsg$* msg) {
+                       msg->clear_$field$();
+                     }
+                     google::protobuf::RepeatedField<$Scalar$>* $getter_mut_thunk$($QualifiedMsg$* msg) {
+                       return msg->mutable_$field$();
+                     }
+                     const google::protobuf::RepeatedField<$Scalar$>& $getter_thunk$($QualifiedMsg$& msg) {
+                       return msg.$field$();
+                     }
+                   )cc");
+             }}},
+           "$impls$");
 }
 
 }  // namespace rust
diff --git a/src/google/protobuf/compiler/rust/accessors/singular_message.cc b/src/google/protobuf/compiler/rust/accessors/singular_message.cc
index 210512a..ca5dce2 100644
--- a/src/google/protobuf/compiler/rust/accessors/singular_message.cc
+++ b/src/google/protobuf/compiler/rust/accessors/singular_message.cc
@@ -17,23 +17,23 @@
 namespace compiler {
 namespace rust {
 
-void SingularMessage::InMsgImpl(Context<FieldDescriptor> field) const {
-  Context<Descriptor> d = field.WithDesc(field.desc().message_type());
+void SingularMessage::InMsgImpl(Context& ctx,
+                                const FieldDescriptor& field) const {
+  auto& msg = *field.message_type();
+  auto prefix = "crate::" + GetCrateRelativeQualifiedPath(ctx, msg);
 
-  auto prefix = "crate::" + GetCrateRelativeQualifiedPath(d);
-
-  field.Emit(
+  ctx.Emit(
       {
           {"prefix", prefix},
-          {"field", field.desc().name()},
-          {"getter_thunk", Thunk(field, "get")},
-          {"getter_mut_thunk", Thunk(field, "get_mut")},
-          {"clearer_thunk", Thunk(field, "clear")},
+          {"field", field.name()},
+          {"getter_thunk", Thunk(ctx, field, "get")},
+          {"getter_mut_thunk", Thunk(ctx, field, "get_mut")},
+          {"clearer_thunk", Thunk(ctx, field, "clear")},
           {
               "view_body",
               [&] {
-                if (field.is_upb()) {
-                  field.Emit({}, R"rs(
+                if (ctx.is_upb()) {
+                  ctx.Emit({}, R"rs(
               let submsg = unsafe { $getter_thunk$(self.inner.msg) };
               // For upb, getters return null if the field is unset, so we need
               // to check for null and return the default instance manually.
@@ -46,7 +46,7 @@
               }
         )rs");
                 } else {
-                  field.Emit({}, R"rs(
+                  ctx.Emit({}, R"rs(
               // For C++ kernel, getters automatically return the
               // default_instance if the field is unset.
               let submsg = unsafe { $getter_thunk$(self.inner.msg) };
@@ -57,15 +57,15 @@
           },
           {"submessage_mut",
            [&] {
-             if (field.is_upb()) {
-               field.Emit({}, R"rs(
+             if (ctx.is_upb()) {
+               ctx.Emit({}, R"rs(
                  let submsg = unsafe {
                    $getter_mut_thunk$(self.inner.msg, self.inner.arena.raw())
                  };
                  $prefix$Mut::new($pbi$::Private, &mut self.inner, submsg)
                  )rs");
              } else {
-               field.Emit({}, R"rs(
+               ctx.Emit({}, R"rs(
                     let submsg = unsafe { $getter_mut_thunk$(self.inner.msg) };
                     $prefix$Mut::new($pbi$::Private, &mut self.inner, submsg)
                   )rs");
@@ -87,21 +87,22 @@
         )rs");
 }
 
-void SingularMessage::InExternC(Context<FieldDescriptor> field) const {
-  field.Emit(
+void SingularMessage::InExternC(Context& ctx,
+                                const FieldDescriptor& field) const {
+  ctx.Emit(
       {
-          {"getter_thunk", Thunk(field, "get")},
-          {"getter_mut_thunk", Thunk(field, "get_mut")},
-          {"clearer_thunk", Thunk(field, "clear")},
+          {"getter_thunk", Thunk(ctx, field, "get")},
+          {"getter_mut_thunk", Thunk(ctx, field, "get_mut")},
+          {"clearer_thunk", Thunk(ctx, field, "clear")},
           {"getter_mut",
            [&] {
-             if (field.is_cpp()) {
-               field.Emit(
+             if (ctx.is_cpp()) {
+               ctx.Emit(
                    R"rs(
                     fn $getter_mut_thunk$(raw_msg: $pbi$::RawMessage)
                        -> $pbi$::RawMessage;)rs");
              } else {
-               field.Emit(
+               ctx.Emit(
                    R"rs(fn $getter_mut_thunk$(raw_msg: $pbi$::RawMessage,
                                                arena: $pbi$::RawArena)
                             -> $pbi$::RawMessage;)rs");
@@ -109,13 +110,13 @@
            }},
           {"ReturnType",
            [&] {
-             if (field.is_cpp()) {
+             if (ctx.is_cpp()) {
                // guaranteed to have a nonnull submsg for the cpp kernel
-               field.Emit({}, "$pbi$::RawMessage;");
+               ctx.Emit({}, "$pbi$::RawMessage;");
              } else {
                // upb kernel may return NULL for a submsg, we can detect this
                // in terra rust if the option returned is None
-               field.Emit({}, "Option<$pbi$::RawMessage>;");
+               ctx.Emit({}, "Option<$pbi$::RawMessage>;");
              }
            }},
       },
@@ -126,22 +127,22 @@
                )rs");
 }
 
-void SingularMessage::InThunkCc(Context<FieldDescriptor> field) const {
-  field.Emit({{"QualifiedMsg",
-               cpp::QualifiedClassName(field.desc().containing_type())},
-              {"getter_thunk", Thunk(field, "get")},
-              {"getter_mut_thunk", Thunk(field, "get_mut")},
-              {"clearer_thunk", Thunk(field, "clear")},
-              {"field", cpp::FieldName(&field.desc())}},
-             R"cc(
-               const void* $getter_thunk$($QualifiedMsg$* msg) {
-                 return static_cast<const void*>(&msg->$field$());
-               }
-               void* $getter_mut_thunk$($QualifiedMsg$* msg) {
-                 return static_cast<void*>(msg->mutable_$field$());
-               }
-               void $clearer_thunk$($QualifiedMsg$* msg) { msg->clear_$field$(); }
-             )cc");
+void SingularMessage::InThunkCc(Context& ctx,
+                                const FieldDescriptor& field) const {
+  ctx.Emit({{"QualifiedMsg", cpp::QualifiedClassName(field.containing_type())},
+            {"getter_thunk", Thunk(ctx, field, "get")},
+            {"getter_mut_thunk", Thunk(ctx, field, "get_mut")},
+            {"clearer_thunk", Thunk(ctx, field, "clear")},
+            {"field", cpp::FieldName(&field)}},
+           R"cc(
+             const void* $getter_thunk$($QualifiedMsg$* msg) {
+               return static_cast<const void*>(&msg->$field$());
+             }
+             void* $getter_mut_thunk$($QualifiedMsg$* msg) {
+               return static_cast<void*>(msg->mutable_$field$());
+             }
+             void $clearer_thunk$($QualifiedMsg$* msg) { msg->clear_$field$(); }
+           )cc");
 }
 
 }  // namespace rust
diff --git a/src/google/protobuf/compiler/rust/accessors/singular_scalar.cc b/src/google/protobuf/compiler/rust/accessors/singular_scalar.cc
index bf4d2c2..48aa4db 100644
--- a/src/google/protobuf/compiler/rust/accessors/singular_scalar.cc
+++ b/src/google/protobuf/compiler/rust/accessors/singular_scalar.cc
@@ -18,16 +18,17 @@
 namespace compiler {
 namespace rust {
 
-void SingularScalar::InMsgImpl(Context<FieldDescriptor> field) const {
-  field.Emit(
+void SingularScalar::InMsgImpl(Context& ctx,
+                               const FieldDescriptor& field) const {
+  ctx.Emit(
       {
-          {"field", field.desc().name()},
-          {"Scalar", PrimitiveRsTypeName(field.desc())},
-          {"hazzer_thunk", Thunk(field, "has")},
+          {"field", field.name()},
+          {"Scalar", PrimitiveRsTypeName(field)},
+          {"hazzer_thunk", Thunk(ctx, field, "has")},
           {"default_value", DefaultValue(field)},
           {"getter",
            [&] {
-             field.Emit({}, R"rs(
+             ctx.Emit({}, R"rs(
                   pub fn r#$field$(&self) -> $Scalar$ {
                     unsafe { $getter_thunk$(self.inner.msg) }
                   }
@@ -35,9 +36,9 @@
            }},
           {"getter_opt",
            [&] {
-             if (!field.desc().is_optional()) return;
-             if (!field.desc().has_presence()) return;
-             field.Emit({}, R"rs(
+             if (!field.is_optional()) return;
+             if (!field.has_presence()) return;
+             ctx.Emit({}, R"rs(
                   pub fn r#$field$_opt(&self) -> $pb$::Optional<$Scalar$> {
                     if !unsafe { $hazzer_thunk$(self.inner.msg) } {
                       return $pb$::Optional::Unset($default_value$);
@@ -47,13 +48,13 @@
                   }
                   )rs");
            }},
-          {"getter_thunk", Thunk(field, "get")},
-          {"setter_thunk", Thunk(field, "set")},
-          {"clearer_thunk", Thunk(field, "clear")},
+          {"getter_thunk", Thunk(ctx, field, "get")},
+          {"setter_thunk", Thunk(ctx, field, "set")},
+          {"clearer_thunk", Thunk(ctx, field, "clear")},
           {"field_mutator_getter",
            [&] {
-             if (field.desc().has_presence()) {
-               field.Emit({}, R"rs(
+             if (field.has_presence()) {
+               ctx.Emit({}, R"rs(
                   pub fn r#$field$_mut(&mut self) -> $pb$::FieldEntry<'_, $Scalar$> {
                     static VTABLE: $pbi$::PrimitiveOptionalMutVTable<$Scalar$> =
                       $pbi$::PrimitiveOptionalMutVTable::new(
@@ -76,7 +77,7 @@
                   }
                 )rs");
              } else {
-               field.Emit({}, R"rs(
+               ctx.Emit({}, R"rs(
                   pub fn r#$field$_mut(&mut self) -> $pb$::Mut<'_, $Scalar$> {
                     static VTABLE: $pbi$::PrimitiveVTable<$Scalar$> =
                       $pbi$::PrimitiveVTable::new(
@@ -114,56 +115,57 @@
         )rs");
 }
 
-void SingularScalar::InExternC(Context<FieldDescriptor> field) const {
-  field.Emit({{"Scalar", PrimitiveRsTypeName(field.desc())},
-              {"hazzer_thunk", Thunk(field, "has")},
-              {"getter_thunk", Thunk(field, "get")},
-              {"setter_thunk", Thunk(field, "set")},
-              {"clearer_thunk", Thunk(field, "clear")},
-              {"hazzer_and_clearer",
-               [&] {
-                 if (field.desc().has_presence()) {
-                   field.Emit(
-                       R"rs(
+void SingularScalar::InExternC(Context& ctx,
+                               const FieldDescriptor& field) const {
+  ctx.Emit({{"Scalar", PrimitiveRsTypeName(field)},
+            {"hazzer_thunk", Thunk(ctx, field, "has")},
+            {"getter_thunk", Thunk(ctx, field, "get")},
+            {"setter_thunk", Thunk(ctx, field, "set")},
+            {"clearer_thunk", Thunk(ctx, field, "clear")},
+            {"hazzer_and_clearer",
+             [&] {
+               if (field.has_presence()) {
+                 ctx.Emit(
+                     R"rs(
                   fn $hazzer_thunk$(raw_msg: $pbi$::RawMessage) -> bool;
                   fn $clearer_thunk$(raw_msg: $pbi$::RawMessage);
                 )rs");
-                 }
-               }}},
-             R"rs(
+               }
+             }}},
+           R"rs(
           $hazzer_and_clearer$
           fn $getter_thunk$(raw_msg: $pbi$::RawMessage) -> $Scalar$;
           fn $setter_thunk$(raw_msg: $pbi$::RawMessage, val: $Scalar$);
         )rs");
 }
 
-void SingularScalar::InThunkCc(Context<FieldDescriptor> field) const {
-  field.Emit({{"field", cpp::FieldName(&field.desc())},
-              {"Scalar", cpp::PrimitiveTypeName(field.desc().cpp_type())},
-              {"QualifiedMsg",
-               cpp::QualifiedClassName(field.desc().containing_type())},
-              {"hazzer_thunk", Thunk(field, "has")},
-              {"getter_thunk", Thunk(field, "get")},
-              {"setter_thunk", Thunk(field, "set")},
-              {"clearer_thunk", Thunk(field, "clear")},
-              {"hazzer_and_clearer",
-               [&] {
-                 if (field.desc().has_presence()) {
-                   field.Emit(R"cc(
-                     bool $hazzer_thunk$($QualifiedMsg$* msg) {
-                       return msg->has_$field$();
-                     }
-                     void $clearer_thunk$($QualifiedMsg$* msg) { msg->clear_$field$(); }
-                   )cc");
-                 }
-               }}},
-             R"cc(
-               $hazzer_and_clearer$;
-               $Scalar$ $getter_thunk$($QualifiedMsg$* msg) { return msg->$field$(); }
-               void $setter_thunk$($QualifiedMsg$* msg, $Scalar$ val) {
-                 msg->set_$field$(val);
+void SingularScalar::InThunkCc(Context& ctx,
+                               const FieldDescriptor& field) const {
+  ctx.Emit({{"field", cpp::FieldName(&field)},
+            {"Scalar", cpp::PrimitiveTypeName(field.cpp_type())},
+            {"QualifiedMsg", cpp::QualifiedClassName(field.containing_type())},
+            {"hazzer_thunk", Thunk(ctx, field, "has")},
+            {"getter_thunk", Thunk(ctx, field, "get")},
+            {"setter_thunk", Thunk(ctx, field, "set")},
+            {"clearer_thunk", Thunk(ctx, field, "clear")},
+            {"hazzer_and_clearer",
+             [&] {
+               if (field.has_presence()) {
+                 ctx.Emit(R"cc(
+                   bool $hazzer_thunk$($QualifiedMsg$* msg) {
+                     return msg->has_$field$();
+                   }
+                   void $clearer_thunk$($QualifiedMsg$* msg) { msg->clear_$field$(); }
+                 )cc");
                }
-             )cc");
+             }}},
+           R"cc(
+             $hazzer_and_clearer$;
+             $Scalar$ $getter_thunk$($QualifiedMsg$* msg) { return msg->$field$(); }
+             void $setter_thunk$($QualifiedMsg$* msg, $Scalar$ val) {
+               msg->set_$field$(val);
+             }
+           )cc");
 }
 
 }  // namespace rust
diff --git a/src/google/protobuf/compiler/rust/accessors/singular_string.cc b/src/google/protobuf/compiler/rust/accessors/singular_string.cc
index f3ae1e2..c70380c 100644
--- a/src/google/protobuf/compiler/rust/accessors/singular_string.cc
+++ b/src/google/protobuf/compiler/rust/accessors/singular_string.cc
@@ -20,24 +20,25 @@
 namespace compiler {
 namespace rust {
 
-void SingularString::InMsgImpl(Context<FieldDescriptor> field) const {
-  std::string hazzer_thunk = Thunk(field, "has");
-  std::string getter_thunk = Thunk(field, "get");
-  std::string setter_thunk = Thunk(field, "set");
-  std::string proxied_type = PrimitiveRsTypeName(field.desc());
+void SingularString::InMsgImpl(Context& ctx,
+                               const FieldDescriptor& field) const {
+  std::string hazzer_thunk = Thunk(ctx, field, "has");
+  std::string getter_thunk = Thunk(ctx, field, "get");
+  std::string setter_thunk = Thunk(ctx, field, "set");
+  std::string proxied_type = PrimitiveRsTypeName(field);
   auto transform_view = [&] {
-    if (field.desc().type() == FieldDescriptor::TYPE_STRING) {
-      field.Emit(R"rs(
+    if (field.type() == FieldDescriptor::TYPE_STRING) {
+      ctx.Emit(R"rs(
         // SAFETY: The runtime doesn't require ProtoStr to be UTF-8.
         unsafe { $pb$::ProtoStr::from_utf8_unchecked(view) }
       )rs");
     } else {
-      field.Emit("view");
+      ctx.Emit("view");
     }
   };
-  field.Emit(
+  ctx.Emit(
       {
-          {"field", field.desc().name()},
+          {"field", field.name()},
           {"hazzer_thunk", hazzer_thunk},
           {"getter_thunk", getter_thunk},
           {"setter_thunk", setter_thunk},
@@ -45,12 +46,12 @@
           {"transform_view", transform_view},
           {"field_optional_getter",
            [&] {
-             if (!field.desc().is_optional()) return;
-             if (!field.desc().has_presence()) return;
-             field.Emit({{"hazzer_thunk", hazzer_thunk},
-                         {"getter_thunk", getter_thunk},
-                         {"transform_view", transform_view}},
-                        R"rs(
+             if (!field.is_optional()) return;
+             if (!field.has_presence()) return;
+             ctx.Emit({{"hazzer_thunk", hazzer_thunk},
+                       {"getter_thunk", getter_thunk},
+                       {"transform_view", transform_view}},
+                      R"rs(
             pub fn $field$_opt(&self) -> $pb$::Optional<&$proxied_type$> {
                 let view = unsafe { $getter_thunk$(self.inner.msg).as_ref() };
                 $pb$::Optional::new(
@@ -62,30 +63,29 @@
            }},
           {"field_mutator_getter",
            [&] {
-             if (field.desc().has_presence()) {
-               field.Emit(
+             if (field.has_presence()) {
+               ctx.Emit(
                    {
-                       {"field", field.desc().name()},
+                       {"field", field.name()},
                        {"proxied_type", proxied_type},
                        {"default_val", DefaultValue(field)},
                        {"view_type", proxied_type},
                        {"transform_field_entry",
                         [&] {
-                          if (field.desc().type() ==
-                              FieldDescriptor::TYPE_STRING) {
-                            field.Emit(R"rs(
+                          if (field.type() == FieldDescriptor::TYPE_STRING) {
+                            ctx.Emit(R"rs(
                               $pb$::ProtoStrMut::field_entry_from_bytes(
                                 $pbi$::Private, out
                               )
                             )rs");
                           } else {
-                            field.Emit("out");
+                            ctx.Emit("out");
                           }
                         }},
                        {"hazzer_thunk", hazzer_thunk},
                        {"getter_thunk", getter_thunk},
                        {"setter_thunk", setter_thunk},
-                       {"clearer_thunk", Thunk(field, "clear")},
+                       {"clearer_thunk", Thunk(ctx, field, "clear")},
                    },
                    R"rs(
             pub fn $field$_mut(&mut self) -> $pb$::FieldEntry<'_, $proxied_type$> {
@@ -112,11 +112,11 @@
             }
           )rs");
              } else {
-               field.Emit({{"field", field.desc().name()},
-                           {"proxied_type", proxied_type},
-                           {"getter_thunk", getter_thunk},
-                           {"setter_thunk", setter_thunk}},
-                          R"rs(
+               ctx.Emit({{"field", field.name()},
+                         {"proxied_type", proxied_type},
+                         {"getter_thunk", getter_thunk},
+                         {"setter_thunk", setter_thunk}},
+                        R"rs(
               pub fn $field$_mut(&mut self) -> $pb$::Mut<'_, $proxied_type$> {
                 static VTABLE: $pbi$::BytesMutVTable = unsafe {
                   $pbi$::BytesMutVTable::new(
@@ -152,20 +152,21 @@
       )rs");
 }
 
-void SingularString::InExternC(Context<FieldDescriptor> field) const {
-  field.Emit({{"hazzer_thunk", Thunk(field, "has")},
-              {"getter_thunk", Thunk(field, "get")},
-              {"setter_thunk", Thunk(field, "set")},
-              {"clearer_thunk", Thunk(field, "clear")},
-              {"hazzer",
-               [&] {
-                 if (field.desc().has_presence()) {
-                   field.Emit(R"rs(
+void SingularString::InExternC(Context& ctx,
+                               const FieldDescriptor& field) const {
+  ctx.Emit({{"hazzer_thunk", Thunk(ctx, field, "has")},
+            {"getter_thunk", Thunk(ctx, field, "get")},
+            {"setter_thunk", Thunk(ctx, field, "set")},
+            {"clearer_thunk", Thunk(ctx, field, "clear")},
+            {"hazzer",
+             [&] {
+               if (field.has_presence()) {
+                 ctx.Emit(R"rs(
                      fn $hazzer_thunk$(raw_msg: $pbi$::RawMessage) -> bool;
                    )rs");
-                 }
-               }}},
-             R"rs(
+               }
+             }}},
+           R"rs(
           $hazzer$
           fn $getter_thunk$(raw_msg: $pbi$::RawMessage) -> $pbi$::PtrAndLen;
           fn $setter_thunk$(raw_msg: $pbi$::RawMessage, val: $pbi$::PtrAndLen);
@@ -173,35 +174,35 @@
         )rs");
 }
 
-void SingularString::InThunkCc(Context<FieldDescriptor> field) const {
-  field.Emit({{"field", cpp::FieldName(&field.desc())},
-              {"QualifiedMsg",
-               cpp::QualifiedClassName(field.desc().containing_type())},
-              {"hazzer_thunk", Thunk(field, "has")},
-              {"getter_thunk", Thunk(field, "get")},
-              {"setter_thunk", Thunk(field, "set")},
-              {"clearer_thunk", Thunk(field, "clear")},
-              {"hazzer",
-               [&] {
-                 if (field.desc().has_presence()) {
-                   field.Emit(R"cc(
-                     bool $hazzer_thunk$($QualifiedMsg$* msg) {
-                       return msg->has_$field$();
-                     }
-                     void $clearer_thunk$($QualifiedMsg$* msg) { msg->clear_$field$(); }
-                   )cc");
-                 }
-               }}},
-             R"cc(
-               $hazzer$;
-               ::google::protobuf::rust_internal::PtrAndLen $getter_thunk$($QualifiedMsg$* msg) {
-                 absl::string_view val = msg->$field$();
-                 return ::google::protobuf::rust_internal::PtrAndLen(val.data(), val.size());
+void SingularString::InThunkCc(Context& ctx,
+                               const FieldDescriptor& field) const {
+  ctx.Emit({{"field", cpp::FieldName(&field)},
+            {"QualifiedMsg", cpp::QualifiedClassName(field.containing_type())},
+            {"hazzer_thunk", Thunk(ctx, field, "has")},
+            {"getter_thunk", Thunk(ctx, field, "get")},
+            {"setter_thunk", Thunk(ctx, field, "set")},
+            {"clearer_thunk", Thunk(ctx, field, "clear")},
+            {"hazzer",
+             [&] {
+               if (field.has_presence()) {
+                 ctx.Emit(R"cc(
+                   bool $hazzer_thunk$($QualifiedMsg$* msg) {
+                     return msg->has_$field$();
+                   }
+                   void $clearer_thunk$($QualifiedMsg$* msg) { msg->clear_$field$(); }
+                 )cc");
                }
-               void $setter_thunk$($QualifiedMsg$* msg, ::google::protobuf::rust_internal::PtrAndLen s) {
-                 msg->set_$field$(absl::string_view(s.ptr, s.len));
-               }
-             )cc");
+             }}},
+           R"cc(
+             $hazzer$;
+             ::google::protobuf::rust_internal::PtrAndLen $getter_thunk$($QualifiedMsg$* msg) {
+               absl::string_view val = msg->$field$();
+               return ::google::protobuf::rust_internal::PtrAndLen(val.data(), val.size());
+             }
+             void $setter_thunk$($QualifiedMsg$* msg, ::google::protobuf::rust_internal::PtrAndLen s) {
+               msg->set_$field$(absl::string_view(s.ptr, s.len));
+             }
+           )cc");
 }
 
 }  // namespace rust
diff --git a/src/google/protobuf/compiler/rust/accessors/unsupported_field.cc b/src/google/protobuf/compiler/rust/accessors/unsupported_field.cc
index e6bce02..591298a 100644
--- a/src/google/protobuf/compiler/rust/accessors/unsupported_field.cc
+++ b/src/google/protobuf/compiler/rust/accessors/unsupported_field.cc
@@ -15,11 +15,12 @@
 namespace compiler {
 namespace rust {
 
-void UnsupportedField::InMsgImpl(Context<FieldDescriptor> field) const {
-  field.Emit({{"reason", reason_}}, R"rs(
+void UnsupportedField::InMsgImpl(Context& ctx,
+                                 const FieldDescriptor& field) const {
+  ctx.Emit({{"reason", reason_}}, R"rs(
     // Unsupported! :( Reason: $reason$
     )rs");
-  field.printer().PrintRaw("\n");
+  ctx.printer().PrintRaw("\n");
 }
 
 }  // namespace rust
diff --git a/src/google/protobuf/compiler/rust/context.cc b/src/google/protobuf/compiler/rust/context.cc
index 68d4c9f..f64e88a 100644
--- a/src/google/protobuf/compiler/rust/context.cc
+++ b/src/google/protobuf/compiler/rust/context.cc
@@ -68,13 +68,12 @@
   return opts;
 }
 
-bool IsInCurrentlyGeneratingCrate(Context<FileDescriptor> file) {
-  return file.generator_context().is_file_in_current_crate(&file.desc());
+bool IsInCurrentlyGeneratingCrate(Context& ctx, const FileDescriptor& file) {
+  return ctx.generator_context().is_file_in_current_crate(file);
 }
 
-bool IsInCurrentlyGeneratingCrate(Context<Descriptor> message) {
-  return message.generator_context().is_file_in_current_crate(
-      message.desc().file());
+bool IsInCurrentlyGeneratingCrate(Context& ctx, const Descriptor& message) {
+  return IsInCurrentlyGeneratingCrate(ctx, *message.file());
 }
 
 }  // namespace rust
diff --git a/src/google/protobuf/compiler/rust/context.h b/src/google/protobuf/compiler/rust/context.h
index affe8ae..1539e2e 100644
--- a/src/google/protobuf/compiler/rust/context.h
+++ b/src/google/protobuf/compiler/rust/context.h
@@ -53,14 +53,14 @@
       const std::vector<const FileDescriptor*>* files_in_current_crate)
       : files_in_current_crate_(*files_in_current_crate) {}
 
-  const FileDescriptor* primary_file() const {
-    return files_in_current_crate_.front();
+  const FileDescriptor& primary_file() const {
+    return *files_in_current_crate_.front();
   }
 
-  bool is_file_in_current_crate(const FileDescriptor* f) const {
+  bool is_file_in_current_crate(const FileDescriptor& f) const {
     return std::find(files_in_current_crate_.begin(),
                      files_in_current_crate_.end(),
-                     f) != files_in_current_crate_.end();
+                     &f) != files_in_current_crate_.end();
   }
 
  private:
@@ -68,26 +68,20 @@
 };
 
 // A context for generating a particular kind of definition.
-// This type acts as an options struct (as in go/totw/173) for most of the
-// generator.
-//
-// `Descriptor` is the type of a descriptor.h class relevant for the current
-// context.
-template <typename Descriptor>
 class Context {
  public:
-  Context(const Options* opts, const Descriptor* desc,
+  Context(const Options* opts,
           const RustGeneratorContext* rust_generator_context,
           io::Printer* printer)
       : opts_(opts),
-        desc_(desc),
         rust_generator_context_(rust_generator_context),
         printer_(printer) {}
 
-  Context(const Context&) = default;
-  Context& operator=(const Context&) = default;
+  Context(const Context&) = delete;
+  Context& operator=(const Context&) = delete;
+  Context(Context&&) = default;
+  Context& operator=(Context&&) = default;
 
-  const Descriptor& desc() const { return *desc_; }
   const Options& opts() const { return *opts_; }
   const RustGeneratorContext& generator_context() const {
     return *rust_generator_context_;
@@ -99,19 +93,8 @@
   // NOTE: prefer ctx.Emit() over ctx.printer().Emit();
   io::Printer& printer() const { return *printer_; }
 
-  // Creates a new context over a different descriptor.
-  template <typename D>
-  Context<D> WithDesc(const D& desc) const {
-    return Context<D>(opts_, &desc, rust_generator_context_, printer_);
-  }
-
-  template <typename D>
-  Context<D> WithDesc(const D* desc) const {
-    return Context<D>(opts_, desc, rust_generator_context_, printer_);
-  }
-
   Context WithPrinter(io::Printer* printer) const {
-    return Context(opts_, desc_, rust_generator_context_, printer);
+    return Context(opts_, rust_generator_context_, printer);
   }
 
   // Forwards to Emit(), which will likely be called all the time.
@@ -128,13 +111,12 @@
 
  private:
   const Options* opts_;
-  const Descriptor* desc_;
   const RustGeneratorContext* rust_generator_context_;
   io::Printer* printer_;
 };
 
-bool IsInCurrentlyGeneratingCrate(Context<FileDescriptor> file);
-bool IsInCurrentlyGeneratingCrate(Context<Descriptor> message);
+bool IsInCurrentlyGeneratingCrate(Context& ctx, const FileDescriptor& file);
+bool IsInCurrentlyGeneratingCrate(Context& ctx, const Descriptor& message);
 
 }  // namespace rust
 }  // namespace compiler
diff --git a/src/google/protobuf/compiler/rust/generator.cc b/src/google/protobuf/compiler/rust/generator.cc
index 3465672..04d5ff6 100644
--- a/src/google/protobuf/compiler/rust/generator.cc
+++ b/src/google/protobuf/compiler/rust/generator.cc
@@ -48,12 +48,11 @@
 //   pub mod submodule {
 //   pub mod separator {
 // ```
-void EmitOpeningOfPackageModules(absl::string_view pkg,
-                                 Context<FileDescriptor> file) {
+void EmitOpeningOfPackageModules(Context& ctx, absl::string_view pkg) {
   if (pkg.empty()) return;
   for (absl::string_view segment : absl::StrSplit(pkg, '.')) {
-    file.Emit({{"segment", segment}},
-              R"rs(
+    ctx.Emit({{"segment", segment}},
+             R"rs(
            pub mod $segment$ {
            )rs");
   }
@@ -70,14 +69,13 @@
 //   } // mod uses
 //   } // mod package
 // ```
-void EmitClosingOfPackageModules(absl::string_view pkg,
-                                 Context<FileDescriptor> file) {
+void EmitClosingOfPackageModules(Context& ctx, absl::string_view pkg) {
   if (pkg.empty()) return;
   std::vector<absl::string_view> segments = absl::StrSplit(pkg, '.');
   absl::c_reverse(segments);
 
   for (absl::string_view segment : segments) {
-    file.Emit({{"segment", segment}}, R"rs(
+    ctx.Emit({{"segment", segment}}, R"rs(
       } // mod $segment$
     )rs");
   }
@@ -87,14 +85,13 @@
 // `non_primary_src` into the `primary_file`.
 //
 // `non_primary_src` has to be a non-primary src of the current `proto_library`.
-void EmitPubUseOfOwnMessages(Context<FileDescriptor>& primary_file,
-                             const Context<FileDescriptor>& non_primary_src) {
-  for (int i = 0; i < non_primary_src.desc().message_type_count(); ++i) {
-    auto msg = primary_file.WithDesc(non_primary_src.desc().message_type(i));
-    auto mod = RustInternalModuleName(non_primary_src);
-    auto name = msg.desc().name();
-    primary_file.Emit({{"mod", mod}, {"Msg", name}},
-                      R"rs(
+void EmitPubUseOfOwnMessages(Context& ctx, const FileDescriptor& primary_file,
+                             const FileDescriptor& non_primary_src) {
+  for (int i = 0; i < non_primary_src.message_type_count(); ++i) {
+    auto& msg = *non_primary_src.message_type(i);
+    auto mod = RustInternalModuleName(ctx, non_primary_src);
+    ctx.Emit({{"mod", mod}, {"Msg", msg.name()}},
+             R"rs(
                         pub use crate::$mod$::$Msg$;
                         // TODO Address use for imported crates
                         pub use crate::$mod$::$Msg$View;
@@ -109,14 +106,15 @@
 //
 // `dep` is a primary src of a dependency of the current `proto_library`.
 // TODO: Add support for public import of non-primary srcs of deps.
-void EmitPubUseForImportedMessages(Context<FileDescriptor>& primary_file,
-                                   const Context<FileDescriptor>& dep) {
-  std::string crate_name = GetCrateName(dep);
-  for (int i = 0; i < dep.desc().message_type_count(); ++i) {
-    auto msg = primary_file.WithDesc(dep.desc().message_type(i));
-    auto path = GetCrateRelativeQualifiedPath(msg);
-    primary_file.Emit({{"crate", crate_name}, {"pkg::Msg", path}},
-                      R"rs(
+void EmitPubUseForImportedMessages(Context& ctx,
+                                   const FileDescriptor& primary_file,
+                                   const FileDescriptor& dep) {
+  std::string crate_name = GetCrateName(ctx, dep);
+  for (int i = 0; i < dep.message_type_count(); ++i) {
+    auto& msg = *dep.message_type(i);
+    auto path = GetCrateRelativeQualifiedPath(ctx, msg);
+    ctx.Emit({{"crate", crate_name}, {"pkg::Msg", path}},
+             R"rs(
                         pub use $crate$::$pkg::Msg$;
                         pub use $crate$::$pkg::Msg$View;
                       )rs");
@@ -124,9 +122,9 @@
 }
 
 // Emits all public imports of the current file
-void EmitPublicImports(Context<FileDescriptor>& primary_file) {
-  for (int i = 0; i < primary_file.desc().public_dependency_count(); ++i) {
-    auto dep_file = primary_file.desc().public_dependency(i);
+void EmitPublicImports(Context& ctx, const FileDescriptor& primary_file) {
+  for (int i = 0; i < primary_file.public_dependency_count(); ++i) {
+    auto& dep_file = *primary_file.public_dependency(i);
     // If the publicly imported file is a src of the current `proto_library`
     // we don't need to emit `pub use` here, we already do it for all srcs in
     // RustGenerator::Generate. In other words, all srcs are implicitly publicly
@@ -134,30 +132,29 @@
     // TODO: Handle the case where a non-primary src with the same
     // declared package as the primary src publicly imports a file that the
     // primary doesn't.
-    auto dep = primary_file.WithDesc(dep_file);
-    if (IsInCurrentlyGeneratingCrate(dep)) {
+    if (IsInCurrentlyGeneratingCrate(ctx, dep_file)) {
       return;
     }
-    EmitPubUseForImportedMessages(primary_file, dep);
+    EmitPubUseForImportedMessages(ctx, primary_file, dep_file);
   }
 }
 
 // Emits submodule declarations so `rustc` can find non primary sources from the
 // primary file.
 void DeclareSubmodulesForNonPrimarySrcs(
-    Context<FileDescriptor>& primary_file,
-    absl::Span<const Context<FileDescriptor>> non_primary_srcs) {
-  std::string primary_file_path = GetRsFile(primary_file);
+    Context& ctx, const FileDescriptor& primary_file,
+    absl::Span<const FileDescriptor* const> non_primary_srcs) {
+  std::string primary_file_path = GetRsFile(ctx, primary_file);
   RelativePath primary_relpath(primary_file_path);
-  for (const auto& non_primary_src : non_primary_srcs) {
-    std::string non_primary_file_path = GetRsFile(non_primary_src);
+  for (const FileDescriptor* non_primary_src : non_primary_srcs) {
+    std::string non_primary_file_path = GetRsFile(ctx, *non_primary_src);
     std::string relative_mod_path =
         primary_relpath.Relative(RelativePath(non_primary_file_path));
-    primary_file.Emit({{"file_path", relative_mod_path},
-                       {"foo", primary_file_path},
-                       {"bar", non_primary_file_path},
-                       {"mod_name", RustInternalModuleName(non_primary_src)}},
-                      R"rs(
+    ctx.Emit({{"file_path", relative_mod_path},
+              {"foo", primary_file_path},
+              {"bar", non_primary_file_path},
+              {"mod_name", RustInternalModuleName(ctx, *non_primary_src)}},
+             R"rs(
                         #[path="$file_path$"]
                         pub mod $mod_name$;
                       )rs");
@@ -169,33 +166,32 @@
 //
 // Returns the non-primary sources that should be reexported from the package of
 // the primary file.
-std::vector<const Context<FileDescriptor>*> ReexportMessagesFromSubmodules(
-    Context<FileDescriptor>& primary_file,
-    absl::Span<const Context<FileDescriptor>> non_primary_srcs) {
-  absl::btree_map<absl::string_view,
-                  std::vector<const Context<FileDescriptor>*>>
+std::vector<const FileDescriptor*> ReexportMessagesFromSubmodules(
+    Context& ctx, const FileDescriptor& primary_file,
+    absl::Span<const FileDescriptor* const> non_primary_srcs) {
+  absl::btree_map<absl::string_view, std::vector<const FileDescriptor*>>
       packages;
-  for (const Context<FileDescriptor>& ctx : non_primary_srcs) {
-    packages[ctx.desc().package()].push_back(&ctx);
+  for (const FileDescriptor* file : non_primary_srcs) {
+    packages[file->package()].push_back(file);
   }
   for (const auto& pair : packages) {
     // We will deal with messages for the package of the primary file later.
     auto fds = pair.second;
-    absl::string_view package = fds[0]->desc().package();
-    if (package == primary_file.desc().package()) continue;
+    absl::string_view package = fds[0]->package();
+    if (package == primary_file.package()) continue;
 
-    EmitOpeningOfPackageModules(package, primary_file);
-    for (const Context<FileDescriptor>* c : fds) {
-      EmitPubUseOfOwnMessages(primary_file, *c);
+    EmitOpeningOfPackageModules(ctx, package);
+    for (const FileDescriptor* c : fds) {
+      EmitPubUseOfOwnMessages(ctx, primary_file, *c);
     }
-    EmitClosingOfPackageModules(package, primary_file);
+    EmitClosingOfPackageModules(ctx, package);
   }
 
-  return packages[primary_file.desc().package()];
+  return packages[primary_file.package()];
 }
 }  // namespace
 
-bool RustGenerator::Generate(const FileDescriptor* file_desc,
+bool RustGenerator::Generate(const FileDescriptor* file,
                              const std::string& parameter,
                              GeneratorContext* generator_context,
                              std::string* error) const {
@@ -210,15 +206,15 @@
 
   RustGeneratorContext rust_generator_context(&files_in_current_crate);
 
-  Context<FileDescriptor> file(&*opts, file_desc, &rust_generator_context,
-                               nullptr);
+  Context ctx_without_printer(&*opts, &rust_generator_context, nullptr);
 
-  auto outfile = absl::WrapUnique(generator_context->Open(GetRsFile(file)));
+  auto outfile = absl::WrapUnique(
+      generator_context->Open(GetRsFile(ctx_without_printer, *file)));
   io::Printer printer(outfile.get());
-  file = file.WithPrinter(&printer);
+  Context ctx = ctx_without_printer.WithPrinter(&printer);
 
   // Convenience shorthands for common symbols.
-  auto v = file.printer().WithVars({
+  auto v = ctx.printer().WithVars({
       {"std", "::__std"},
       {"pb", "::__pb"},
       {"pbi", "::__pb::__internal"},
@@ -227,67 +223,66 @@
       {"Phantom", "::__std::marker::PhantomData"},
   });
 
-  file.Emit({{"kernel", KernelRsName(file.opts().kernel)}}, R"rs(
+  ctx.Emit({{"kernel", KernelRsName(ctx.opts().kernel)}}, R"rs(
     extern crate protobuf_$kernel$ as __pb;
     extern crate std as __std;
 
   )rs");
 
-  std::vector<Context<FileDescriptor>> file_contexts;
+  std::vector<const FileDescriptor*> file_contexts;
   for (const FileDescriptor* f : files_in_current_crate) {
-    file_contexts.push_back(file.WithDesc(*f));
+    file_contexts.push_back(f);
   }
 
   // Generating the primary file?
-  if (file_desc == rust_generator_context.primary_file()) {
+  if (file == &rust_generator_context.primary_file()) {
     auto non_primary_srcs = absl::MakeConstSpan(file_contexts).subspan(1);
-    DeclareSubmodulesForNonPrimarySrcs(file, non_primary_srcs);
+    DeclareSubmodulesForNonPrimarySrcs(ctx, *file, non_primary_srcs);
 
-    std::vector<const Context<FileDescriptor>*>
-        non_primary_srcs_in_primary_package =
-            ReexportMessagesFromSubmodules(file, non_primary_srcs);
+    std::vector<const FileDescriptor*> non_primary_srcs_in_primary_package =
+        ReexportMessagesFromSubmodules(ctx, *file, non_primary_srcs);
 
-    EmitOpeningOfPackageModules(file.desc().package(), file);
+    EmitOpeningOfPackageModules(ctx, file->package());
 
-    for (const Context<FileDescriptor>* non_primary_file :
+    for (const FileDescriptor* non_primary_file :
          non_primary_srcs_in_primary_package) {
-      EmitPubUseOfOwnMessages(file, *non_primary_file);
+      EmitPubUseOfOwnMessages(ctx, *file, *non_primary_file);
     }
   }
 
-  EmitPublicImports(file);
+  EmitPublicImports(ctx, *file);
 
   std::unique_ptr<io::ZeroCopyOutputStream> thunks_cc;
   std::unique_ptr<io::Printer> thunks_printer;
-  if (file.is_cpp()) {
-    thunks_cc.reset(generator_context->Open(GetThunkCcFile(file)));
+  if (ctx.is_cpp()) {
+    thunks_cc.reset(generator_context->Open(GetThunkCcFile(ctx, *file)));
     thunks_printer = std::make_unique<io::Printer>(thunks_cc.get());
 
-    thunks_printer->Emit({{"proto_h", GetHeaderFile(file)}},
+    thunks_printer->Emit({{"proto_h", GetHeaderFile(ctx, *file)}},
                          R"cc(
 #include "$proto_h$"
 #include "google/protobuf/rust/cpp_kernel/cpp_api.h"
                          )cc");
   }
 
-  for (int i = 0; i < file.desc().message_type_count(); ++i) {
-    auto msg = file.WithDesc(file.desc().message_type(i));
+  for (int i = 0; i < file->message_type_count(); ++i) {
+    auto& msg = *file->message_type(i);
 
-    GenerateRs(msg);
-    msg.printer().PrintRaw("\n");
+    GenerateRs(ctx, msg);
+    ctx.printer().PrintRaw("\n");
 
-    if (file.is_cpp()) {
-      auto thunks_msg = msg.WithPrinter(thunks_printer.get());
+    if (ctx.is_cpp()) {
+      auto thunks_ctx = ctx.WithPrinter(thunks_printer.get());
 
-      thunks_msg.Emit({{"Msg", msg.desc().full_name()}}, R"cc(
+      thunks_ctx.Emit({{"Msg", msg.full_name()}}, R"cc(
         // $Msg$
       )cc");
-      GenerateThunksCc(thunks_msg);
-      thunks_msg.printer().PrintRaw("\n");
+      GenerateThunksCc(thunks_ctx, msg);
+      thunks_ctx.printer().PrintRaw("\n");
     }
   }
-  if (file_desc == files_in_current_crate.front()) {
-    EmitClosingOfPackageModules(file.desc().package(), file);
+  if (file == files_in_current_crate.front()) {
+    EmitClosingOfPackageModules(ctx, file->package());
   }
   return true;
 }
diff --git a/src/google/protobuf/compiler/rust/message.cc b/src/google/protobuf/compiler/rust/message.cc
index 256cf81..6f5454d 100644
--- a/src/google/protobuf/compiler/rust/message.cc
+++ b/src/google/protobuf/compiler/rust/message.cc
@@ -24,16 +24,16 @@
 namespace rust {
 namespace {
 
-void MessageNew(Context<Descriptor> msg) {
-  switch (msg.opts().kernel) {
+void MessageNew(Context& ctx, const Descriptor& msg) {
+  switch (ctx.opts().kernel) {
     case Kernel::kCpp:
-      msg.Emit({{"new_thunk", Thunk(msg, "new")}}, R"rs(
+      ctx.Emit({{"new_thunk", Thunk(ctx, msg, "new")}}, R"rs(
         Self { inner: $pbr$::MessageInner { msg: unsafe { $new_thunk$() } } }
       )rs");
       return;
 
     case Kernel::kUpb:
-      msg.Emit({{"new_thunk", Thunk(msg, "new")}}, R"rs(
+      ctx.Emit({{"new_thunk", Thunk(ctx, msg, "new")}}, R"rs(
         let arena = $pbr$::Arena::new();
         Self {
           inner: $pbr$::MessageInner {
@@ -48,16 +48,16 @@
   ABSL_LOG(FATAL) << "unreachable";
 }
 
-void MessageSerialize(Context<Descriptor> msg) {
-  switch (msg.opts().kernel) {
+void MessageSerialize(Context& ctx, const Descriptor& msg) {
+  switch (ctx.opts().kernel) {
     case Kernel::kCpp:
-      msg.Emit({{"serialize_thunk", Thunk(msg, "serialize")}}, R"rs(
+      ctx.Emit({{"serialize_thunk", Thunk(ctx, msg, "serialize")}}, R"rs(
         unsafe { $serialize_thunk$(self.inner.msg) }
       )rs");
       return;
 
     case Kernel::kUpb:
-      msg.Emit({{"serialize_thunk", Thunk(msg, "serialize")}}, R"rs(
+      ctx.Emit({{"serialize_thunk", Thunk(ctx, msg, "serialize")}}, R"rs(
         let arena = $pbr$::Arena::new();
         let mut len = 0;
         unsafe {
@@ -71,12 +71,12 @@
   ABSL_LOG(FATAL) << "unreachable";
 }
 
-void MessageDeserialize(Context<Descriptor> msg) {
-  switch (msg.opts().kernel) {
+void MessageDeserialize(Context& ctx, const Descriptor& msg) {
+  switch (ctx.opts().kernel) {
     case Kernel::kCpp:
-      msg.Emit(
+      ctx.Emit(
           {
-              {"deserialize_thunk", Thunk(msg, "deserialize")},
+              {"deserialize_thunk", Thunk(ctx, msg, "deserialize")},
           },
           R"rs(
           let success = unsafe {
@@ -92,7 +92,7 @@
       return;
 
     case Kernel::kUpb:
-      msg.Emit({{"deserialize_thunk", Thunk(msg, "parse")}}, R"rs(
+      ctx.Emit({{"deserialize_thunk", Thunk(ctx, msg, "parse")}}, R"rs(
         let arena = $pbr$::Arena::new();
         let msg = unsafe {
           $deserialize_thunk$(data.as_ptr(), data.len(), arena.raw())
@@ -115,15 +115,15 @@
   ABSL_LOG(FATAL) << "unreachable";
 }
 
-void MessageExterns(Context<Descriptor> msg) {
-  switch (msg.opts().kernel) {
+void MessageExterns(Context& ctx, const Descriptor& msg) {
+  switch (ctx.opts().kernel) {
     case Kernel::kCpp:
-      msg.Emit(
+      ctx.Emit(
           {
-              {"new_thunk", Thunk(msg, "new")},
-              {"delete_thunk", Thunk(msg, "delete")},
-              {"serialize_thunk", Thunk(msg, "serialize")},
-              {"deserialize_thunk", Thunk(msg, "deserialize")},
+              {"new_thunk", Thunk(ctx, msg, "new")},
+              {"delete_thunk", Thunk(ctx, msg, "delete")},
+              {"serialize_thunk", Thunk(ctx, msg, "serialize")},
+              {"deserialize_thunk", Thunk(ctx, msg, "deserialize")},
           },
           R"rs(
           fn $new_thunk$() -> $pbi$::RawMessage;
@@ -134,11 +134,11 @@
       return;
 
     case Kernel::kUpb:
-      msg.Emit(
+      ctx.Emit(
           {
-              {"new_thunk", Thunk(msg, "new")},
-              {"serialize_thunk", Thunk(msg, "serialize")},
-              {"deserialize_thunk", Thunk(msg, "parse")},
+              {"new_thunk", Thunk(ctx, msg, "new")},
+              {"serialize_thunk", Thunk(ctx, msg, "serialize")},
+              {"deserialize_thunk", Thunk(ctx, msg, "parse")},
           },
           R"rs(
           fn $new_thunk$(arena: $pbi$::RawArena) -> $pbi$::RawMessage;
@@ -151,36 +151,37 @@
   ABSL_LOG(FATAL) << "unreachable";
 }
 
-void MessageDrop(Context<Descriptor> msg) {
-  if (msg.is_upb()) {
+void MessageDrop(Context& ctx, const Descriptor& msg) {
+  if (ctx.is_upb()) {
     // Nothing to do here; drop glue (which will run drop(self.arena)
     // automatically) is sufficient.
     return;
   }
 
-  msg.Emit({{"delete_thunk", Thunk(msg, "delete")}}, R"rs(
+  ctx.Emit({{"delete_thunk", Thunk(ctx, msg, "delete")}}, R"rs(
     unsafe { $delete_thunk$(self.inner.msg); }
   )rs");
 }
 
-void GetterForViewOrMut(Context<FieldDescriptor> field, bool is_mut) {
-  auto fieldName = field.desc().name();
-  auto fieldType = field.desc().type();
-  auto getter_thunk = Thunk(field, "get");
-  auto setter_thunk = Thunk(field, "set");
-  auto clearer_thunk = Thunk(field, "clear");
+void GetterForViewOrMut(Context& ctx, const FieldDescriptor& field,
+                        bool is_mut) {
+  auto fieldName = field.name();
+  auto fieldType = field.type();
+  auto getter_thunk = Thunk(ctx, field, "get");
+  auto setter_thunk = Thunk(ctx, field, "set");
+  auto clearer_thunk = Thunk(ctx, field, "clear");
   // If we're dealing with a Mut, the getter must be supplied
   // self.inner.msg() whereas a View has to be supplied self.msg
   auto self = is_mut ? "self.inner.msg()" : "self.msg";
 
   if (fieldType == FieldDescriptor::TYPE_MESSAGE) {
-    Context<Descriptor> d = field.WithDesc(field.desc().message_type());
+    const Descriptor& msg = *field.message_type();
     // TODO: support messages which are defined in other crates.
-    if (!IsInCurrentlyGeneratingCrate(d)) {
+    if (!IsInCurrentlyGeneratingCrate(ctx, msg)) {
       return;
     }
-    auto prefix = "crate::" + GetCrateRelativeQualifiedPath(d);
-    field.Emit(
+    auto prefix = "crate::" + GetCrateRelativeQualifiedPath(ctx, msg);
+    ctx.Emit(
         {
             {"prefix", prefix},
             {"field", fieldName},
@@ -190,8 +191,8 @@
             {
                 "view_body",
                 [&] {
-                  if (field.is_upb()) {
-                    field.Emit({}, R"rs(
+                  if (ctx.is_upb()) {
+                    ctx.Emit({}, R"rs(
                       let submsg = unsafe { $getter_thunk$($self$) };
                       match submsg {
                         None => $prefix$View::new($pbi$::Private,
@@ -200,7 +201,7 @@
                       }
                 )rs");
                   } else {
-                    field.Emit({}, R"rs(
+                    ctx.Emit({}, R"rs(
                       let submsg = unsafe { $getter_thunk$($self$) };
                       $prefix$View::new($pbi$::Private, submsg)
                 )rs");
@@ -216,18 +217,18 @@
     return;
   }
 
-  auto rsType = PrimitiveRsTypeName(field.desc());
+  auto rsType = PrimitiveRsTypeName(field);
   if (fieldType == FieldDescriptor::TYPE_STRING ||
       fieldType == FieldDescriptor::TYPE_BYTES) {
-    field.Emit({{"field", fieldName},
-                {"self", self},
-                {"getter_thunk", getter_thunk},
-                {"setter_thunk", setter_thunk},
-                {"RsType", rsType},
-                {"maybe_mutator",
-                 [&] {
-                   if (is_mut) {
-                     field.Emit({}, R"rs(
+    ctx.Emit({{"field", fieldName},
+              {"self", self},
+              {"getter_thunk", getter_thunk},
+              {"setter_thunk", setter_thunk},
+              {"RsType", rsType},
+              {"maybe_mutator",
+               [&] {
+                 if (is_mut) {
+                   ctx.Emit({}, R"rs(
                     pub fn r#$field$_mut(&self) -> $pb$::Mut<'_, $RsType$> {
                        static VTABLE: $pbi$::BytesMutVTable = 
                         $pbi$::BytesMutVTable::new(
@@ -248,9 +249,9 @@
                       }
                     }
                     )rs");
-                   }
-                 }}},
-               R"rs(
+                 }
+               }}},
+             R"rs(
               pub fn r#$field$(&self) -> $pb$::View<'_, $RsType$> {
                 let s = unsafe { $getter_thunk$($self$).as_ref() };
                 unsafe { __pb::ProtoStr::from_utf8_unchecked(s).into() }
@@ -259,19 +260,19 @@
               $maybe_mutator$
             )rs");
   } else {
-    field.Emit({{"field", fieldName},
-                {"getter_thunk", getter_thunk},
-                {"setter_thunk", setter_thunk},
-                {"clearer_thunk", clearer_thunk},
-                {"self", self},
-                {"RsType", rsType},
-                {"maybe_mutator",
-                 [&] {
-                   // TODO: once the rust public api is accessible,
-                   // by tooling, ensure that this only appears for the
-                   // mutational pathway
-                   if (is_mut && fieldType) {
-                     field.Emit({}, R"rs(
+    ctx.Emit({{"field", fieldName},
+              {"getter_thunk", getter_thunk},
+              {"setter_thunk", setter_thunk},
+              {"clearer_thunk", clearer_thunk},
+              {"self", self},
+              {"RsType", rsType},
+              {"maybe_mutator",
+               [&] {
+                 // TODO: once the rust public api is accessible,
+                 // by tooling, ensure that this only appears for the
+                 // mutational pathway
+                 if (is_mut && fieldType) {
+                   ctx.Emit({}, R"rs(
                     pub fn r#$field$_mut(&self) -> $pb$::Mut<'_, $RsType$> {
                       static VTABLE: $pbi$::PrimitiveVTable<$RsType$> =
                         $pbi$::PrimitiveVTable::new(
@@ -290,9 +291,9 @@
                       }
                     }
                     )rs");
-                   }
-                 }}},
-               R"rs(
+                 }
+               }}},
+             R"rs(
             pub fn r#$field$(&self) -> $pb$::View<'_, $RsType$> {
               unsafe { $getter_thunk$($self$) }
             }
@@ -302,93 +303,89 @@
   }
 }
 
-void AccessorsForViewOrMut(Context<Descriptor> msg, bool is_mut) {
-  for (int i = 0; i < msg.desc().field_count(); ++i) {
-    auto field = msg.WithDesc(*msg.desc().field(i));
-    if (field.desc().is_repeated()) continue;
+void AccessorsForViewOrMut(Context& ctx, const Descriptor& msg, bool is_mut) {
+  for (int i = 0; i < msg.field_count(); ++i) {
+    const FieldDescriptor& field = *msg.field(i);
+    if (field.is_repeated()) continue;
     // TODO - add cord support
-    if (field.desc().options().has_ctype()) continue;
+    if (field.options().has_ctype()) continue;
     // TODO
-    if (field.desc().type() == FieldDescriptor::TYPE_ENUM ||
-        field.desc().type() == FieldDescriptor::TYPE_GROUP)
+    if (field.type() == FieldDescriptor::TYPE_ENUM ||
+        field.type() == FieldDescriptor::TYPE_GROUP)
       continue;
-    GetterForViewOrMut(field, is_mut);
-    msg.printer().PrintRaw("\n");
+    GetterForViewOrMut(ctx, field, is_mut);
+    ctx.printer().PrintRaw("\n");
   }
 }
 
 }  // namespace
 
-void GenerateRs(Context<Descriptor> msg) {
-  if (msg.desc().map_key() != nullptr) {
-    ABSL_LOG(WARNING) << "unsupported map field: " << msg.desc().full_name();
+void GenerateRs(Context& ctx, const Descriptor& msg) {
+  if (msg.map_key() != nullptr) {
+    ABSL_LOG(WARNING) << "unsupported map field: " << msg.full_name();
     return;
   }
-  msg.Emit(
-      {{"Msg", msg.desc().name()},
-       {"Msg::new", [&] { MessageNew(msg); }},
-       {"Msg::serialize", [&] { MessageSerialize(msg); }},
-       {"Msg::deserialize", [&] { MessageDeserialize(msg); }},
-       {"Msg::drop", [&] { MessageDrop(msg); }},
-       {"Msg_externs", [&] { MessageExterns(msg); }},
-       {"accessor_fns",
-        [&] {
-          for (int i = 0; i < msg.desc().field_count(); ++i) {
-            auto field = msg.WithDesc(*msg.desc().field(i));
-            msg.Emit({{"comment", FieldInfoComment(field)}}, R"rs(
+  ctx.Emit({{"Msg", msg.name()},
+            {"Msg::new", [&] { MessageNew(ctx, msg); }},
+            {"Msg::serialize", [&] { MessageSerialize(ctx, msg); }},
+            {"Msg::deserialize", [&] { MessageDeserialize(ctx, msg); }},
+            {"Msg::drop", [&] { MessageDrop(ctx, msg); }},
+            {"Msg_externs", [&] { MessageExterns(ctx, msg); }},
+            {"accessor_fns",
+             [&] {
+               for (int i = 0; i < msg.field_count(); ++i) {
+                 auto& field = *msg.field(i);
+                 ctx.Emit({{"comment", FieldInfoComment(ctx, field)}}, R"rs(
                  // $comment$
                )rs");
-            GenerateAccessorMsgImpl(field);
-            msg.printer().PrintRaw("\n");
-          }
-        }},
-       {"oneof_accessor_fns",
-        [&] {
-          for (int i = 0; i < msg.desc().real_oneof_decl_count(); ++i) {
-            GenerateOneofAccessors(
-                msg.WithDesc(*msg.desc().real_oneof_decl(i)));
-            msg.printer().PrintRaw("\n");
-          }
-        }},
-       {"accessor_externs",
-        [&] {
-          for (int i = 0; i < msg.desc().field_count(); ++i) {
-            GenerateAccessorExternC(msg.WithDesc(*msg.desc().field(i)));
-            msg.printer().PrintRaw("\n");
-          }
-        }},
-       {"oneof_externs",
-        [&] {
-          for (int i = 0; i < msg.desc().real_oneof_decl_count(); ++i) {
-            GenerateOneofExternC(msg.WithDesc(*msg.desc().real_oneof_decl(i)));
-            msg.printer().PrintRaw("\n");
-          }
-        }},
-       {"nested_msgs",
-        [&] {
-          // If we have no nested types or oneofs, bail out without emitting
-          // an empty mod SomeMsg_.
-          if (msg.desc().nested_type_count() == 0 &&
-              msg.desc().real_oneof_decl_count() == 0) {
-            return;
-          }
-          msg.Emit(
-              {{"Msg", msg.desc().name()},
-               {"nested_msgs",
-                [&] {
-                  for (int i = 0; i < msg.desc().nested_type_count(); ++i) {
-                    auto nested_msg = msg.WithDesc(msg.desc().nested_type(i));
-                    GenerateRs(nested_msg);
-                  }
-                }},
-               {"oneofs",
-                [&] {
-                  for (int i = 0; i < msg.desc().real_oneof_decl_count(); ++i) {
-                    GenerateOneofDefinition(
-                        msg.WithDesc(*msg.desc().real_oneof_decl(i)));
-                  }
-                }}},
-              R"rs(
+                 GenerateAccessorMsgImpl(ctx, field);
+                 ctx.printer().PrintRaw("\n");
+               }
+             }},
+            {"oneof_accessor_fns",
+             [&] {
+               for (int i = 0; i < msg.real_oneof_decl_count(); ++i) {
+                 GenerateOneofAccessors(ctx, *msg.real_oneof_decl(i));
+                 ctx.printer().PrintRaw("\n");
+               }
+             }},
+            {"accessor_externs",
+             [&] {
+               for (int i = 0; i < msg.field_count(); ++i) {
+                 GenerateAccessorExternC(ctx, *msg.field(i));
+                 ctx.printer().PrintRaw("\n");
+               }
+             }},
+            {"oneof_externs",
+             [&] {
+               for (int i = 0; i < msg.real_oneof_decl_count(); ++i) {
+                 GenerateOneofExternC(ctx, *msg.real_oneof_decl(i));
+                 ctx.printer().PrintRaw("\n");
+               }
+             }},
+            {"nested_msgs",
+             [&] {
+               // If we have no nested types or oneofs, bail out without
+               // emitting an empty mod SomeMsg_.
+               if (msg.nested_type_count() == 0 &&
+                   msg.real_oneof_decl_count() == 0) {
+                 return;
+               }
+               ctx.Emit(
+                   {{"Msg", msg.name()},
+                    {"nested_msgs",
+                     [&] {
+                       for (int i = 0; i < msg.nested_type_count(); ++i) {
+                         GenerateRs(ctx, *msg.nested_type(i));
+                       }
+                     }},
+                    {"oneofs",
+                     [&] {
+                       for (int i = 0; i < msg.real_oneof_decl_count(); ++i) {
+                         GenerateOneofDefinition(ctx, *msg.real_oneof_decl(i));
+                       }
+                     }}},
+                   R"rs(
                  #[allow(non_snake_case)]
                  pub mod $Msg$_ {
                    $nested_msgs$
@@ -396,10 +393,12 @@
                    $oneofs$
                  }  // mod $Msg$_
                 )rs");
-        }},
-       {"accessor_fns_for_views", [&] { AccessorsForViewOrMut(msg, false); }},
-       {"accessor_fns_for_muts", [&] { AccessorsForViewOrMut(msg, true); }}},
-      R"rs(
+             }},
+            {"accessor_fns_for_views",
+             [&] { AccessorsForViewOrMut(ctx, msg, false); }},
+            {"accessor_fns_for_muts",
+             [&] { AccessorsForViewOrMut(ctx, msg, true); }}},
+           R"rs(
         #[allow(non_camel_case_types)]
         // TODO: Implement support for debug redaction
         #[derive(Debug)]
@@ -542,9 +541,9 @@
         $nested_msgs$
       )rs");
 
-  if (msg.is_cpp()) {
-    msg.printer().PrintRaw("\n");
-    msg.Emit({{"Msg", msg.desc().name()}}, R"rs(
+  if (ctx.is_cpp()) {
+    ctx.printer().PrintRaw("\n");
+    ctx.Emit({{"Msg", msg.name()}}, R"rs(
       impl $Msg$ {
         pub fn __unstable_wrap_cpp_grant_permission_to_break(msg: $pbi$::RawMessage) -> Self {
           Self { inner: $pbr$::MessageInner { msg } }
@@ -558,39 +557,37 @@
 }
 
 // Generates code for a particular message in `.pb.thunk.cc`.
-void GenerateThunksCc(Context<Descriptor> msg) {
-  ABSL_CHECK(msg.is_cpp());
-  if (msg.desc().map_key() != nullptr) {
-    ABSL_LOG(WARNING) << "unsupported map field: " << msg.desc().full_name();
+void GenerateThunksCc(Context& ctx, const Descriptor& msg) {
+  ABSL_CHECK(ctx.is_cpp());
+  if (msg.map_key() != nullptr) {
+    ABSL_LOG(WARNING) << "unsupported map field: " << msg.full_name();
     return;
   }
 
-  msg.Emit(
+  ctx.Emit(
       {{"abi", "\"C\""},  // Workaround for syntax highlight bug in VSCode.
-       {"Msg", msg.desc().name()},
-       {"QualifiedMsg", cpp::QualifiedClassName(&msg.desc())},
-       {"new_thunk", Thunk(msg, "new")},
-       {"delete_thunk", Thunk(msg, "delete")},
-       {"serialize_thunk", Thunk(msg, "serialize")},
-       {"deserialize_thunk", Thunk(msg, "deserialize")},
+       {"Msg", msg.name()},
+       {"QualifiedMsg", cpp::QualifiedClassName(&msg)},
+       {"new_thunk", Thunk(ctx, msg, "new")},
+       {"delete_thunk", Thunk(ctx, msg, "delete")},
+       {"serialize_thunk", Thunk(ctx, msg, "serialize")},
+       {"deserialize_thunk", Thunk(ctx, msg, "deserialize")},
        {"nested_msg_thunks",
         [&] {
-          for (int i = 0; i < msg.desc().nested_type_count(); ++i) {
-            Context<Descriptor> nested_msg =
-                msg.WithDesc(msg.desc().nested_type(i));
-            GenerateThunksCc(nested_msg);
+          for (int i = 0; i < msg.nested_type_count(); ++i) {
+            GenerateThunksCc(ctx, *msg.nested_type(i));
           }
         }},
        {"accessor_thunks",
         [&] {
-          for (int i = 0; i < msg.desc().field_count(); ++i) {
-            GenerateAccessorThunkCc(msg.WithDesc(*msg.desc().field(i)));
+          for (int i = 0; i < msg.field_count(); ++i) {
+            GenerateAccessorThunkCc(ctx, *msg.field(i));
           }
         }},
        {"oneof_thunks",
         [&] {
-          for (int i = 0; i < msg.desc().real_oneof_decl_count(); ++i) {
-            GenerateOneofThunkCc(msg.WithDesc(*msg.desc().real_oneof_decl(i)));
+          for (int i = 0; i < msg.real_oneof_decl_count(); ++i) {
+            GenerateOneofThunkCc(ctx, *msg.real_oneof_decl(i));
           }
         }}},
       R"cc(
diff --git a/src/google/protobuf/compiler/rust/message.h b/src/google/protobuf/compiler/rust/message.h
index 4d1c392..e2734eb 100644
--- a/src/google/protobuf/compiler/rust/message.h
+++ b/src/google/protobuf/compiler/rust/message.h
@@ -21,10 +21,10 @@
 namespace rust {
 
 // Generates code for a particular message in `.pb.rs`.
-void GenerateRs(Context<Descriptor> msg);
+void GenerateRs(Context& ctx, const Descriptor& msg);
 
 // Generates code for a particular message in `.pb.thunk.cc`.
-void GenerateThunksCc(Context<Descriptor> msg);
+void GenerateThunksCc(Context& ctx, const Descriptor& msg);
 
 }  // namespace rust
 }  // namespace compiler
diff --git a/src/google/protobuf/compiler/rust/naming.cc b/src/google/protobuf/compiler/rust/naming.cc
index 6685493..12fb089 100644
--- a/src/google/protobuf/compiler/rust/naming.cc
+++ b/src/google/protobuf/compiler/rust/naming.cc
@@ -26,22 +26,23 @@
 namespace compiler {
 namespace rust {
 namespace {
-std::string GetUnderscoreDelimitedFullName(Context<Descriptor> msg) {
-  std::string result = msg.desc().full_name();
+std::string GetUnderscoreDelimitedFullName(Context& ctx,
+                                           const Descriptor& msg) {
+  std::string result = msg.full_name();
   absl::StrReplaceAll({{".", "_"}}, &result);
   return result;
 }
 }  // namespace
 
-std::string GetCrateName(Context<FileDescriptor> dep) {
-  absl::string_view path = dep.desc().name();
+std::string GetCrateName(Context& ctx, const FileDescriptor& dep) {
+  absl::string_view path = dep.name();
   auto basename = path.substr(path.rfind('/') + 1);
   return absl::StrReplaceAll(basename, {{".", "_"}, {"-", "_"}});
 }
 
-std::string GetRsFile(Context<FileDescriptor> file) {
-  auto basename = StripProto(file.desc().name());
-  switch (auto k = file.opts().kernel) {
+std::string GetRsFile(Context& ctx, const FileDescriptor& file) {
+  auto basename = StripProto(file.name());
+  switch (auto k = ctx.opts().kernel) {
     case Kernel::kUpb:
       return absl::StrCat(basename, ".u.pb.rs");
     case Kernel::kCpp:
@@ -52,42 +53,41 @@
   }
 }
 
-std::string GetThunkCcFile(Context<FileDescriptor> file) {
-  auto basename = StripProto(file.desc().name());
+std::string GetThunkCcFile(Context& ctx, const FileDescriptor& file) {
+  auto basename = StripProto(file.name());
   return absl::StrCat(basename, ".pb.thunks.cc");
 }
 
-std::string GetHeaderFile(Context<FileDescriptor> file) {
-  auto basename = StripProto(file.desc().name());
+std::string GetHeaderFile(Context& ctx, const FileDescriptor& file) {
+  auto basename = StripProto(file.name());
   return absl::StrCat(basename, ".proto.h");
 }
 
 namespace {
 
 template <typename T>
-std::string FieldPrefix(Context<T> field) {
-  // NOTE: When field.is_upb(), this functions outputs must match the symbols
+std::string FieldPrefix(Context& ctx, const T& field) {
+  // NOTE: When ctx.is_upb(), this functions outputs must match the symbols
   // that the upbc plugin generates exactly. Failure to do so correctly results
   // in a link-time failure.
-  absl::string_view prefix = field.is_cpp() ? "__rust_proto_thunk__" : "";
-  std::string thunk_prefix =
-      absl::StrCat(prefix, GetUnderscoreDelimitedFullName(
-                               field.WithDesc(field.desc().containing_type())));
+  absl::string_view prefix = ctx.is_cpp() ? "__rust_proto_thunk__" : "";
+  std::string thunk_prefix = absl::StrCat(
+      prefix, GetUnderscoreDelimitedFullName(ctx, *field.containing_type()));
   return thunk_prefix;
 }
 
 template <typename T>
-std::string Thunk(Context<T> field, absl::string_view op) {
-  std::string thunk = FieldPrefix(field);
+std::string Thunk(Context& ctx, const T& field, absl::string_view op) {
+  std::string thunk = FieldPrefix(ctx, field);
 
   absl::string_view format;
-  if (field.is_upb() && op == "get") {
+  if (ctx.is_upb() && op == "get") {
     // upb getter is simply the field name (no "get" in the name).
     format = "_$1";
-  } else if (field.is_upb() && op == "get_mut") {
+  } else if (ctx.is_upb() && op == "get_mut") {
     // same as above, with with `mutable` prefix
     format = "_mutable_$1";
-  } else if (field.is_upb() && op == "case") {
+  } else if (ctx.is_upb() && op == "case") {
     // some upb functions are in the order x_op compared to has/set/clear which
     // are in the other order e.g. op_x.
     format = "_$1_$0";
@@ -95,51 +95,53 @@
     format = "_$0_$1";
   }
 
-  absl::SubstituteAndAppend(&thunk, format, op, field.desc().name());
+  absl::SubstituteAndAppend(&thunk, format, op, field.name());
   return thunk;
 }
 
-std::string ThunkMapOrRepeated(Context<FieldDescriptor> field,
+std::string ThunkMapOrRepeated(Context& ctx, const FieldDescriptor& field,
                                absl::string_view op) {
-  if (!field.is_upb()) {
-    return Thunk<FieldDescriptor>(field, op);
+  if (!ctx.is_upb()) {
+    return Thunk<FieldDescriptor>(ctx, field, op);
   }
 
-  std::string thunk = absl::StrCat("_", FieldPrefix(field));
+  std::string thunk = absl::StrCat("_", FieldPrefix(ctx, field));
   absl::string_view format;
   if (op == "get") {
-    format = field.desc().is_map() ? "_$1_upb_map" : "_$1_upb_array";
+    format = field.is_map() ? "_$1_upb_map" : "_$1_upb_array";
   } else if (op == "get_mut") {
-    format =
-        field.desc().is_map() ? "_$1_mutable_upb_map" : "_$1_mutable_upb_array";
+    format = field.is_map() ? "_$1_mutable_upb_map" : "_$1_mutable_upb_array";
   } else {
-    return Thunk<FieldDescriptor>(field, op);
+    return Thunk<FieldDescriptor>(ctx, field, op);
   }
 
-  absl::SubstituteAndAppend(&thunk, format, op, field.desc().name());
+  absl::SubstituteAndAppend(&thunk, format, op, field.name());
   return thunk;
 }
 
 }  // namespace
 
-std::string Thunk(Context<FieldDescriptor> field, absl::string_view op) {
-  if (field.desc().is_map() || field.desc().is_repeated()) {
-    return ThunkMapOrRepeated(field, op);
+std::string Thunk(Context& ctx, const FieldDescriptor& field,
+                  absl::string_view op) {
+  if (field.is_map() || field.is_repeated()) {
+    return ThunkMapOrRepeated(ctx, field, op);
   }
-  return Thunk<FieldDescriptor>(field, op);
+  return Thunk<FieldDescriptor>(ctx, field, op);
 }
 
-std::string Thunk(Context<OneofDescriptor> field, absl::string_view op) {
-  return Thunk<OneofDescriptor>(field, op);
+std::string Thunk(Context& ctx, const OneofDescriptor& field,
+                  absl::string_view op) {
+  return Thunk<OneofDescriptor>(ctx, field, op);
 }
 
-std::string Thunk(Context<Descriptor> msg, absl::string_view op) {
-  absl::string_view prefix = msg.is_cpp() ? "__rust_proto_thunk__" : "";
-  return absl::StrCat(prefix, GetUnderscoreDelimitedFullName(msg), "_", op);
+std::string Thunk(Context& ctx, const Descriptor& msg, absl::string_view op) {
+  absl::string_view prefix = ctx.is_cpp() ? "__rust_proto_thunk__" : "";
+  return absl::StrCat(prefix, GetUnderscoreDelimitedFullName(ctx, msg), "_",
+                      op);
 }
 
-std::string PrimitiveRsTypeName(const FieldDescriptor& desc) {
-  switch (desc.type()) {
+std::string PrimitiveRsTypeName(const FieldDescriptor& field) {
+  switch (field.type()) {
     case FieldDescriptor::TYPE_BOOL:
       return "bool";
     case FieldDescriptor::TYPE_INT32:
@@ -167,7 +169,7 @@
     default:
       break;
   }
-  ABSL_LOG(FATAL) << "Unsupported field type: " << desc.type_name();
+  ABSL_LOG(FATAL) << "Unsupported field type: " << field.type_name();
   return "";
 }
 
@@ -180,20 +182,18 @@
 //
 // If the message has no package and no containing messages then this returns
 // empty string.
-std::string RustModule(Context<Descriptor> msg) {
-  const Descriptor& desc = msg.desc();
-
+std::string RustModule(Context& ctx, const Descriptor& msg) {
   std::vector<std::string> modules;
 
   std::vector<std::string> package_modules =
-      absl::StrSplit(desc.file()->package(), '.', absl::SkipEmpty());
+      absl::StrSplit(msg.file()->package(), '.', absl::SkipEmpty());
 
   modules.insert(modules.begin(), package_modules.begin(),
                  package_modules.end());
 
   // Innermost to outermost order.
   std::vector<std::string> modules_from_containing_types;
-  const Descriptor* parent = desc.containing_type();
+  const Descriptor* parent = msg.containing_type();
   while (parent != nullptr) {
     modules_from_containing_types.push_back(absl::StrCat(parent->name(), "_"));
     parent = parent->containing_type();
@@ -213,27 +213,25 @@
   return absl::StrJoin(modules, "::");
 }
 
-std::string RustInternalModuleName(Context<FileDescriptor> file) {
+std::string RustInternalModuleName(Context& ctx, const FileDescriptor& file) {
   // TODO: Introduce a more robust mangling here to avoid conflicts
   // between `foo/bar/baz.proto` and `foo_bar/baz.proto`.
-  return absl::StrReplaceAll(StripProto(file.desc().name()), {{"/", "_"}});
+  return absl::StrReplaceAll(StripProto(file.name()), {{"/", "_"}});
 }
 
-std::string GetCrateRelativeQualifiedPath(Context<Descriptor> msg) {
-  return absl::StrCat(RustModule(msg), msg.desc().name());
+std::string GetCrateRelativeQualifiedPath(Context& ctx, const Descriptor& msg) {
+  return absl::StrCat(RustModule(ctx, msg), msg.name());
 }
 
-std::string FieldInfoComment(Context<FieldDescriptor> field) {
-  absl::string_view label =
-      field.desc().is_repeated() ? "repeated" : "optional";
-  std::string comment =
-      absl::StrCat(field.desc().name(), ": ", label, " ",
-                   FieldDescriptor::TypeName(field.desc().type()));
+std::string FieldInfoComment(Context& ctx, const FieldDescriptor& field) {
+  absl::string_view label = field.is_repeated() ? "repeated" : "optional";
+  std::string comment = absl::StrCat(field.name(), ": ", label, " ",
+                                     FieldDescriptor::TypeName(field.type()));
 
-  if (auto* m = field.desc().message_type()) {
+  if (auto* m = field.message_type()) {
     absl::StrAppend(&comment, " ", m->full_name());
   }
-  if (auto* m = field.desc().enum_type()) {
+  if (auto* m = field.enum_type()) {
     absl::StrAppend(&comment, " ", m->full_name());
   }
 
diff --git a/src/google/protobuf/compiler/rust/naming.h b/src/google/protobuf/compiler/rust/naming.h
index 2bb5cc5..e6f7b7f 100644
--- a/src/google/protobuf/compiler/rust/naming.h
+++ b/src/google/protobuf/compiler/rust/naming.h
@@ -19,25 +19,27 @@
 namespace protobuf {
 namespace compiler {
 namespace rust {
-std::string GetCrateName(Context<FileDescriptor> dep);
+std::string GetCrateName(Context& ctx, const FileDescriptor& dep);
 
-std::string GetRsFile(Context<FileDescriptor> file);
-std::string GetThunkCcFile(Context<FileDescriptor> file);
-std::string GetHeaderFile(Context<FileDescriptor> file);
+std::string GetRsFile(Context& ctx, const FileDescriptor& file);
+std::string GetThunkCcFile(Context& ctx, const FileDescriptor& file);
+std::string GetHeaderFile(Context& ctx, const FileDescriptor& file);
 
-std::string Thunk(Context<FieldDescriptor> field, absl::string_view op);
-std::string Thunk(Context<OneofDescriptor> field, absl::string_view op);
+std::string Thunk(Context& ctx, const FieldDescriptor& field,
+                  absl::string_view op);
+std::string Thunk(Context& ctx, const OneofDescriptor& field,
+                  absl::string_view op);
 
-std::string Thunk(Context<Descriptor> msg, absl::string_view op);
+std::string Thunk(Context& ctx, const Descriptor& msg, absl::string_view op);
 
-std::string PrimitiveRsTypeName(const FieldDescriptor& desc);
+std::string PrimitiveRsTypeName(const FieldDescriptor& field);
 
-std::string FieldInfoComment(Context<FieldDescriptor> field);
+std::string FieldInfoComment(Context& ctx, const FieldDescriptor& field);
 
-std::string RustModule(Context<Descriptor> msg);
-std::string RustInternalModuleName(Context<FileDescriptor> file);
+std::string RustModule(Context& ctx, const Descriptor& msg);
+std::string RustInternalModuleName(Context& ctx, const FileDescriptor& file);
 
-std::string GetCrateRelativeQualifiedPath(Context<Descriptor> msg);
+std::string GetCrateRelativeQualifiedPath(Context& ctx, const Descriptor& msg);
 
 }  // namespace rust
 }  // namespace compiler
diff --git a/src/google/protobuf/compiler/rust/oneof.cc b/src/google/protobuf/compiler/rust/oneof.cc
index d6166ae..ce6e3af 100644
--- a/src/google/protobuf/compiler/rust/oneof.cc
+++ b/src/google/protobuf/compiler/rust/oneof.cc
@@ -78,27 +78,25 @@
   return cpp::UnderscoresToCamelCase(name, /* upper initial letter */ true);
 }
 
-std::string oneofViewEnumRsName(const OneofDescriptor& desc) {
-  return ToCamelCase(desc.name());
+std::string oneofViewEnumRsName(const OneofDescriptor& oneof) {
+  return ToCamelCase(oneof.name());
 }
 
-std::string oneofMutEnumRsName(const OneofDescriptor& desc) {
-  return ToCamelCase(desc.name()) + "Mut";
+std::string oneofMutEnumRsName(const OneofDescriptor& oneof) {
+  return ToCamelCase(oneof.name()) + "Mut";
 }
 
-std::string oneofCaseEnumName(const OneofDescriptor& desc) {
+std::string oneofCaseEnumName(const OneofDescriptor& oneof) {
   // Note: This is the name used for the cpp Case enum, we use it for both
   // the Rust Case enum as well as for the cpp case enum in the cpp thunk.
-  return ToCamelCase(desc.name()) + "Case";
+  return ToCamelCase(oneof.name()) + "Case";
 }
 
-std::string RsTypeNameView(Context<FieldDescriptor> field) {
-  const auto& desc = field.desc();
-
-  if (desc.options().has_ctype()) {
+std::string RsTypeNameView(Context& ctx, const FieldDescriptor& field) {
+  if (field.options().has_ctype()) {
     return "";  // TODO: b/308792377 - ctype fields not supported yet.
   }
-  switch (desc.type()) {
+  switch (field.type()) {
     case FieldDescriptor::TYPE_INT32:
     case FieldDescriptor::TYPE_INT64:
     case FieldDescriptor::TYPE_FIXED32:
@@ -112,7 +110,7 @@
     case FieldDescriptor::TYPE_FLOAT:
     case FieldDescriptor::TYPE_DOUBLE:
     case FieldDescriptor::TYPE_BOOL:
-      return PrimitiveRsTypeName(desc);
+      return PrimitiveRsTypeName(field);
     case FieldDescriptor::TYPE_BYTES:
       return "&'msg [u8]";
     case FieldDescriptor::TYPE_STRING:
@@ -120,23 +118,21 @@
     case FieldDescriptor::TYPE_MESSAGE:
       return absl::StrCat(
           "::__pb::View<'msg, crate::",
-          GetCrateRelativeQualifiedPath(field.WithDesc(desc.message_type())),
-          ">");
+          GetCrateRelativeQualifiedPath(ctx, *field.message_type()), ">");
     case FieldDescriptor::TYPE_ENUM:   // TODO: b/300257770 - Support enums.
     case FieldDescriptor::TYPE_GROUP:  // Not supported yet.
       return "";
   }
 
-  ABSL_LOG(FATAL) << "Unexpected field type: " << desc.type_name();
+  ABSL_LOG(FATAL) << "Unexpected field type: " << field.type_name();
   return "";
 }
 
-std::string RsTypeNameMut(Context<FieldDescriptor> field) {
-  const auto& desc = field.desc();
-  if (desc.options().has_ctype()) {
+std::string RsTypeNameMut(Context& ctx, const FieldDescriptor& field) {
+  if (field.options().has_ctype()) {
     return "";  // TODO: b/308792377 - ctype fields not supported yet.
   }
-  switch (desc.type()) {
+  switch (field.type()) {
     case FieldDescriptor::TYPE_INT32:
     case FieldDescriptor::TYPE_INT64:
     case FieldDescriptor::TYPE_FIXED32:
@@ -152,56 +148,54 @@
     case FieldDescriptor::TYPE_BOOL:
     case FieldDescriptor::TYPE_BYTES:
     case FieldDescriptor::TYPE_STRING:
-      return absl::StrCat("::__pb::Mut<'msg, ", PrimitiveRsTypeName(desc), ">");
+      return absl::StrCat("::__pb::Mut<'msg, ", PrimitiveRsTypeName(field),
+                          ">");
     case FieldDescriptor::TYPE_MESSAGE:
       return absl::StrCat(
           "::__pb::Mut<'msg, crate::",
-          GetCrateRelativeQualifiedPath(field.WithDesc(desc.message_type())),
-          ">");
+          GetCrateRelativeQualifiedPath(ctx, *field.message_type()), ">");
     case FieldDescriptor::TYPE_ENUM:   // TODO: b/300257770 - Support enums.
     case FieldDescriptor::TYPE_GROUP:  // Not supported yet.
       return "";
   }
 
-  ABSL_LOG(FATAL) << "Unexpected field type: " << desc.type_name();
+  ABSL_LOG(FATAL) << "Unexpected field type: " << field.type_name();
   return "";
 }
 
 }  // namespace
 
-void GenerateOneofDefinition(Context<OneofDescriptor> oneof) {
-  const auto& desc = oneof.desc();
-
-  oneof.Emit(
-      {{"view_enum_name", oneofViewEnumRsName(desc)},
-       {"mut_enum_name", oneofMutEnumRsName(desc)},
+void GenerateOneofDefinition(Context& ctx, const OneofDescriptor& oneof) {
+  ctx.Emit(
+      {{"view_enum_name", oneofViewEnumRsName(oneof)},
+       {"mut_enum_name", oneofMutEnumRsName(oneof)},
        {"view_fields",
         [&] {
-          for (int i = 0; i < desc.field_count(); ++i) {
-            const auto& field = *desc.field(i);
-            std::string rs_type = RsTypeNameView(oneof.WithDesc(field));
+          for (int i = 0; i < oneof.field_count(); ++i) {
+            auto& field = *oneof.field(i);
+            std::string rs_type = RsTypeNameView(ctx, field);
             if (rs_type.empty()) {
               continue;
             }
-            oneof.Emit({{"name", ToCamelCase(field.name())},
-                        {"type", rs_type},
-                        {"number", std::to_string(field.number())}},
-                       R"rs($name$($type$) = $number$,
+            ctx.Emit({{"name", ToCamelCase(field.name())},
+                      {"type", rs_type},
+                      {"number", std::to_string(field.number())}},
+                     R"rs($name$($type$) = $number$,
                 )rs");
           }
         }},
        {"mut_fields",
         [&] {
-          for (int i = 0; i < desc.field_count(); ++i) {
-            const auto& field = *desc.field(i);
-            std::string rs_type = RsTypeNameMut(oneof.WithDesc(field));
+          for (int i = 0; i < oneof.field_count(); ++i) {
+            auto& field = *oneof.field(i);
+            std::string rs_type = RsTypeNameMut(ctx, field);
             if (rs_type.empty()) {
               continue;
             }
-            oneof.Emit({{"name", ToCamelCase(field.name())},
-                        {"type", rs_type},
-                        {"number", std::to_string(field.number())}},
-                       R"rs($name$($type$) = $number$,
+            ctx.Emit({{"name", ToCamelCase(field.name())},
+                      {"type", rs_type},
+                      {"number", std::to_string(field.number())}},
+                     R"rs($name$($type$) = $number$,
                 )rs");
           }
         }}},
@@ -236,18 +230,18 @@
 
   // Note: This enum is used as the Thunk return type for getting which case is
   // used: it exactly matches the generate case enum that both cpp and upb use.
-  oneof.Emit({{"case_enum_name", oneofCaseEnumName(desc)},
-              {"cases",
-               [&] {
-                 for (int i = 0; i < desc.field_count(); ++i) {
-                   const auto& field = desc.field(i);
-                   oneof.Emit({{"name", ToCamelCase(field->name())},
-                               {"number", std::to_string(field->number())}},
-                              R"rs($name$ = $number$,
+  ctx.Emit({{"case_enum_name", oneofCaseEnumName(oneof)},
+            {"cases",
+             [&] {
+               for (int i = 0; i < oneof.field_count(); ++i) {
+                 auto& field = *oneof.field(i);
+                 ctx.Emit({{"name", ToCamelCase(field.name())},
+                           {"number", std::to_string(field.number())}},
+                          R"rs($name$ = $number$,
                 )rs");
-                 }
-               }}},
-             R"rs(
+               }
+             }}},
+           R"rs(
       #[repr(C)]
       #[derive(Debug, Copy, Clone, PartialEq, Eq)]
       pub(super) enum $case_enum_name$ {
@@ -260,23 +254,21 @@
       )rs");
 }
 
-void GenerateOneofAccessors(Context<OneofDescriptor> oneof) {
-  const auto& desc = oneof.desc();
-
-  oneof.Emit(
-      {{"oneof_name", desc.name()},
-       {"view_enum_name", oneofViewEnumRsName(desc)},
-       {"mut_enum_name", oneofMutEnumRsName(desc)},
-       {"case_enum_name", oneofCaseEnumName(desc)},
+void GenerateOneofAccessors(Context& ctx, const OneofDescriptor& oneof) {
+  ctx.Emit(
+      {{"oneof_name", oneof.name()},
+       {"view_enum_name", oneofViewEnumRsName(oneof)},
+       {"mut_enum_name", oneofMutEnumRsName(oneof)},
+       {"case_enum_name", oneofCaseEnumName(oneof)},
        {"view_cases",
         [&] {
-          for (int i = 0; i < desc.field_count(); ++i) {
-            const auto& field = *desc.field(i);
-            std::string rs_type = RsTypeNameView(oneof.WithDesc(field));
+          for (int i = 0; i < oneof.field_count(); ++i) {
+            auto& field = *oneof.field(i);
+            std::string rs_type = RsTypeNameView(ctx, field);
             if (rs_type.empty()) {
               continue;
             }
-            oneof.Emit(
+            ctx.Emit(
                 {
                     {"case", ToCamelCase(field.name())},
                     {"rs_getter", field.name()},
@@ -288,13 +280,13 @@
         }},
        {"mut_cases",
         [&] {
-          for (int i = 0; i < desc.field_count(); ++i) {
-            const auto& field = *desc.field(i);
-            std::string rs_type = RsTypeNameMut(oneof.WithDesc(field));
+          for (int i = 0; i < oneof.field_count(); ++i) {
+            auto& field = *oneof.field(i);
+            std::string rs_type = RsTypeNameMut(ctx, field);
             if (rs_type.empty()) {
               continue;
             }
-            oneof.Emit(
+            ctx.Emit(
                 {{"case", ToCamelCase(field.name())},
                  {"rs_mut_getter", field.name() + "_mut"},
                  {"type", rs_type},
@@ -321,7 +313,7 @@
                $Msg$_::$mut_enum_name$::$case$(self.$rs_mut_getter$()$into_mut_transform$), )rs");
           }
         }},
-       {"case_thunk", Thunk(oneof, "case")}},
+       {"case_thunk", Thunk(ctx, oneof, "case")}},
       R"rs(
         pub fn r#$oneof_name$(&self) -> $Msg$_::$view_enum_name$ {
           match unsafe { $case_thunk$(self.inner.msg) } {
@@ -340,26 +332,24 @@
       )rs");
 }
 
-void GenerateOneofExternC(Context<OneofDescriptor> oneof) {
-  const auto& desc = oneof.desc();
-  oneof.Emit(
+void GenerateOneofExternC(Context& ctx, const OneofDescriptor& oneof) {
+  ctx.Emit(
       {
-          {"case_enum_rs_name", oneofCaseEnumName(desc)},
-          {"case_thunk", Thunk(oneof, "case")},
+          {"case_enum_rs_name", oneofCaseEnumName(oneof)},
+          {"case_thunk", Thunk(ctx, oneof, "case")},
       },
       R"rs(
         fn $case_thunk$(raw_msg: $pbi$::RawMessage) -> $Msg$_::$case_enum_rs_name$;
       )rs");
 }
 
-void GenerateOneofThunkCc(Context<OneofDescriptor> oneof) {
-  const auto& desc = oneof.desc();
-  oneof.Emit(
+void GenerateOneofThunkCc(Context& ctx, const OneofDescriptor& oneof) {
+  ctx.Emit(
       {
-          {"oneof_name", desc.name()},
-          {"case_enum_name", oneofCaseEnumName(desc)},
-          {"case_thunk", Thunk(oneof, "case")},
-          {"QualifiedMsg", cpp::QualifiedClassName(desc.containing_type())},
+          {"oneof_name", oneof.name()},
+          {"case_enum_name", oneofCaseEnumName(oneof)},
+          {"case_thunk", Thunk(ctx, oneof, "case")},
+          {"QualifiedMsg", cpp::QualifiedClassName(oneof.containing_type())},
       },
       R"cc(
         $QualifiedMsg$::$case_enum_name$ $case_thunk$($QualifiedMsg$* msg) {
diff --git a/src/google/protobuf/compiler/rust/oneof.h b/src/google/protobuf/compiler/rust/oneof.h
index 98e2bc9..7ad2143 100644
--- a/src/google/protobuf/compiler/rust/oneof.h
+++ b/src/google/protobuf/compiler/rust/oneof.h
@@ -16,10 +16,10 @@
 namespace compiler {
 namespace rust {
 
-void GenerateOneofDefinition(Context<OneofDescriptor> oneof);
-void GenerateOneofAccessors(Context<OneofDescriptor> oneof);
-void GenerateOneofExternC(Context<OneofDescriptor> oneof);
-void GenerateOneofThunkCc(Context<OneofDescriptor> oneof);
+void GenerateOneofDefinition(Context& ctx, const OneofDescriptor& oneof);
+void GenerateOneofAccessors(Context& ctx, const OneofDescriptor& oneof);
+void GenerateOneofExternC(Context& ctx, const OneofDescriptor& oneof);
+void GenerateOneofThunkCc(Context& ctx, const OneofDescriptor& oneof);
 
 }  // namespace rust
 }  // namespace compiler