diff --git a/gcc/rust/Make-lang.in b/gcc/rust/Make-lang.in
index af5d775a3a6..f166b02340d 100644
--- a/gcc/rust/Make-lang.in
+++ b/gcc/rust/Make-lang.in
@@ -118,6 +118,7 @@ GRS_OBJS = \
     rust/rust-ast-lower-expr.o \
     rust/rust-ast-lower-type.o \
     rust/rust-ast-lower-stmt.o \
+    rust/rust-ast-lower-format-args.o \
     rust/rust-rib.o \
     rust/rust-name-resolution-context.o \
     rust/rust-default-resolver.o \
diff --git a/gcc/rust/ast/rust-ast-collector.cc b/gcc/rust/ast/rust-ast-collector.cc
index b8ec62367bc..c0e8e774824 100644
--- a/gcc/rust/ast/rust-ast-collector.cc
+++ b/gcc/rust/ast/rust-ast-collector.cc
@@ -2810,7 +2810,8 @@ TokenCollector::visit (BareFunctionType &type)
 void
 TokenCollector::visit (AST::FormatArgs &fmt)
 {
-  rust_sorry_at (0, "unimplemented format_args!() visitor");
+  rust_sorry_at (fmt.get_locus (), "%s:%u: unimplemented FormatArgs visitor",
+		 __FILE__, __LINE__);
 }
 
 } // namespace AST
diff --git a/gcc/rust/ast/rust-ast.cc b/gcc/rust/ast/rust-ast.cc
index f3dabc6cd0f..fbd795f6718 100644
--- a/gcc/rust/ast/rust-ast.cc
+++ b/gcc/rust/ast/rust-ast.cc
@@ -5065,7 +5065,7 @@ FormatArgs::as_string () const
 location_t
 FormatArgs::get_locus () const
 {
-  rust_unreachable ();
+  return loc;
 }
 
 bool
diff --git a/gcc/rust/ast/rust-builtin-ast-nodes.h b/gcc/rust/ast/rust-builtin-ast-nodes.h
index 6e267173a55..780d1a9d4e9 100644
--- a/gcc/rust/ast/rust-builtin-ast-nodes.h
+++ b/gcc/rust/ast/rust-builtin-ast-nodes.h
@@ -184,48 +184,32 @@ public:
 
   FormatArgs (location_t loc, Fmt::Pieces &&template_str,
 	      FormatArguments &&arguments)
-    : loc (loc), template_str (std::move (template_str)),
+    : loc (loc), template_pieces (std::move (template_str)),
       arguments (std::move (arguments))
   {}
 
-  FormatArgs (FormatArgs &&other)
-    : loc (std::move (other.loc)),
-      template_str (std::move (other.template_str)),
-      arguments (std::move (other.arguments))
-  {
-    std::cerr << "[ARTHUR] moving FormatArgs" << std::endl;
-  }
-
-  // FIXME: This might be invalid - we are reusing the same memory allocated
-  // on the Rust side for `other`. This is probably valid as long as we only
-  // ever read that memory and never write to it.
-  FormatArgs (const FormatArgs &other)
-    : loc (other.loc), template_str (other.template_str),
-      arguments (other.arguments)
-  {
-    std::cerr << "[ARTHUR] copying FormatArgs" << std::endl;
-  }
-
-  // FormatArgs &operator= (const FormatArgs &other) = default;
-  //   : template_str (other.template_str), arguments (other.arguments)
-  // {}
+  FormatArgs (FormatArgs &&other) = default;
+  FormatArgs (const FormatArgs &other) = default;
+  FormatArgs &operator= (const FormatArgs &other) = default;
 
   void accept_vis (AST::ASTVisitor &vis) override;
 
+  const Fmt::Pieces &get_template () const { return template_pieces; }
+  virtual location_t get_locus () const override;
+
 private:
   location_t loc;
   // FIXME: This probably needs to be a separate type - it is one in rustc's
   // expansion of format_args!(). There is extra handling associated with it.
   // we can maybe do that in rust-fmt.cc? in collect_pieces()? like do the
   // transformation into something we can handle better
-  Fmt::Pieces template_str;
+  Fmt::Pieces template_pieces;
   FormatArguments arguments;
 
   bool marked_for_strip = false;
 
 protected:
   virtual std::string as_string () const override;
-  virtual location_t get_locus () const override;
   virtual bool is_expr_without_block () const override;
   virtual void mark_for_strip () override;
   virtual bool is_marked_for_strip () const override;
diff --git a/gcc/rust/ast/rust-fmt.cc b/gcc/rust/ast/rust-fmt.cc
index c367e30d546..b82e089fc41 100644
--- a/gcc/rust/ast/rust-fmt.cc
+++ b/gcc/rust/ast/rust-fmt.cc
@@ -27,30 +27,23 @@ Pieces::collect (std::string &&to_parse, bool append_newline)
 {
   auto piece_slice = collect_pieces (to_parse.c_str (), append_newline);
 
-  rust_debug ("[ARTHUR] %p, %lu", (const void *) piece_slice.base_ptr,
-	      piece_slice.len);
-
   // this performs multiple copies, can we avoid them maybe?
-  // auto pieces = std::vector<Piece> (piece_slice.base_ptr,
-  // 	     piece_slice.base_ptr + piece_slice.len);
-
-  return Pieces (piece_slice, std::move (to_parse));
+  // TODO: Instead of just creating a vec of, basically, `ffi::Piece`s, we
+  // should transform them into the proper C++ type which we can work with. so
+  // transform all the strings into C++ strings? all the Option<T> into
+  // tl::optional<T>?
+  auto pieces = std::vector<Piece> (piece_slice.base_ptr,
+				    piece_slice.base_ptr + piece_slice.len);
+
+  return Pieces (std::move (pieces), piece_slice, std::move (to_parse));
 }
 
-Pieces::~Pieces ()
-{
-  std::cerr << "Arthur: destoying pieces. this: " << (void *) this
-	    << " slice: " << slice.base_ptr << std::endl;
-  destroy_pieces (slice);
-}
+Pieces::~Pieces () { destroy_pieces (slice); }
 
-Pieces::Pieces (const Pieces &other) : to_parse (other.to_parse)
+Pieces::Pieces (const Pieces &other)
+  : pieces_vector (other.pieces_vector), to_parse (other.to_parse)
 {
   slice = clone_pieces (other.slice.base_ptr, other.slice.len, other.slice.cap);
-  std::cerr << "Arthur: copying pieces: other.to_parse: "
-	    << (void *) other.to_parse.c_str ()
-	    << " ours to_parse: " << (void *) to_parse.c_str () << std::endl;
-  // auto pieces = std::vector (slice.base_ptr, slice.base_ptr + slice.len);
 }
 
 Pieces &
@@ -63,13 +56,11 @@ Pieces::operator= (const Pieces &other)
 }
 
 Pieces::Pieces (Pieces &&other)
-  : slice (
-    clone_pieces (other.slice.base_ptr, other.slice.len, other.slice.cap)),
+  : pieces_vector (std::move (other.pieces_vector)),
+    slice (
+      clone_pieces (other.slice.base_ptr, other.slice.len, other.slice.cap)),
     to_parse (std::move (other.to_parse))
-{
-  std::cerr << "Arthur: moving pieces. to_parse: " << (void *) to_parse.c_str ()
-	    << " base_ptr/slice: " << (void *) slice.base_ptr << std::endl;
-}
+{}
 
 } // namespace Fmt
 } // namespace Rust
diff --git a/gcc/rust/ast/rust-fmt.h b/gcc/rust/ast/rust-fmt.h
index 22447c4eba0..ba412f9958c 100644
--- a/gcc/rust/ast/rust-fmt.h
+++ b/gcc/rust/ast/rust-fmt.h
@@ -262,6 +262,8 @@ struct Pieces
 
   Pieces (Pieces &&other);
 
+  const std::vector<Piece> &get_pieces () const { return pieces_vector; }
+
   // {
   //   slice = clone_pieces (&other.slice);
   //   to_parse = other.to_parse;
@@ -270,10 +272,17 @@ struct Pieces
   // }
 
 private:
-  Pieces (PieceSlice slice, std::string &&to_parse)
-    : slice (slice), to_parse (std::move (to_parse))
+  Pieces (std::vector<Piece> &&pieces_vector, PieceSlice slice,
+	  std::string &&to_parse)
+    : pieces_vector (std::move (pieces_vector)), slice (slice),
+      to_parse (std::move (to_parse))
   {}
 
+  std::vector<Piece> pieces_vector;
+
+  // this memory is held for FFI reasons - it needs to be released and cloned
+  // precisely, so try to not access it/modify it if possible. you should
+  // instead work with `pieces_vector`
   PieceSlice slice;
   std::string to_parse;
 };
diff --git a/gcc/rust/hir/rust-ast-lower-expr.cc b/gcc/rust/hir/rust-ast-lower-expr.cc
index 8e07b19df4c..ece15529b5f 100644
--- a/gcc/rust/hir/rust-ast-lower-expr.cc
+++ b/gcc/rust/hir/rust-ast-lower-expr.cc
@@ -19,6 +19,7 @@
 #include "rust-ast-lower-expr.h"
 #include "rust-ast-lower-base.h"
 #include "rust-ast-lower-block.h"
+#include "rust-ast-lower-format-args.h"
 #include "rust-ast-lower-struct-field-expr.h"
 #include "rust-ast-lower-pattern.h"
 #include "rust-ast-lower-type.h"
@@ -833,7 +834,11 @@ ASTLoweringExpr::visit (AST::ClosureExprInnerTyped &expr)
 void
 ASTLoweringExpr::visit (AST::FormatArgs &fmt)
 {
-  rust_sorry_at (0, "unimplemented format_args!() visitor");
+  // Lowering FormatArgs is complex, and this file is already very long
+  translated = FormatArgsLowering ().go (fmt);
+
+  rust_sorry_at (fmt.get_locus (),
+		 "FormatArgs lowering is not implemented yet");
 }
 
 } // namespace HIR
diff --git a/gcc/rust/hir/rust-ast-lower-format-args.cc b/gcc/rust/hir/rust-ast-lower-format-args.cc
new file mode 100644
index 00000000000..fcae55ad8c4
--- /dev/null
+++ b/gcc/rust/hir/rust-ast-lower-format-args.cc
@@ -0,0 +1,41 @@
+// Copyright (C) 2024 Free Software Foundation, Inc.
+
+// This file is part of GCC.
+
+// GCC is free software; you can redistribute it and/or modify it under
+// the terms of the GNU General Public License as published by the Free
+// Software Foundation; either version 3, or (at your option) any later
+// version.
+
+// GCC is distributed in the hope that it will be useful, but WITHOUT ANY
+// WARRANTY; without even the implied warranty of MERCHANTABILITY or
+// FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+// for more details.
+
+// You should have received a copy of the GNU General Public License
+// along with GCC; see the file COPYING3.  If not see
+// <http://www.gnu.org/licenses/>.
+
+#include "rust-ast-lower-format-args.h"
+#include "rust-ast-full.h"
+#include "rust-hir-full.h"
+
+namespace Rust {
+namespace HIR {
+
+FormatArgsLowering::FormatArgsLowering () {}
+
+HIR::Expr *
+FormatArgsLowering::go (AST::FormatArgs &fmt)
+{
+  // Eventually, we will ned to perform format_args!() expansion as part of HIR
+  // lowering - this enables a couple of interesting optimizations such as
+  // format_args flattening and the inlining of constants into the format
+  // strings. However, this is not a priority at the moment and it is easier to
+  // do "regular" macro expansion for `format_arsg!()`
+
+  return nullptr;
+}
+
+} // namespace HIR
+} // namespace Rust
diff --git a/gcc/rust/hir/rust-ast-lower-format-args.h b/gcc/rust/hir/rust-ast-lower-format-args.h
new file mode 100644
index 00000000000..f21635c4d0a
--- /dev/null
+++ b/gcc/rust/hir/rust-ast-lower-format-args.h
@@ -0,0 +1,40 @@
+// Copyright (C) 2024 Free Software Foundation, Inc.
+
+// This file is part of GCC.
+
+// GCC is free software; you can redistribute it and/or modify it under
+// the terms of the GNU General Public License as published by the Free
+// Software Foundation; either version 3, or (at your option) any later
+// version.
+
+// GCC is distributed in the hope that it will be useful, but WITHOUT ANY
+// WARRANTY; without even the implied warranty of MERCHANTABILITY or
+// FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+// for more details.
+
+// You should have received a copy of the GNU General Public License
+// along with GCC; see the file COPYING3.  If not see
+// <http://www.gnu.org/licenses/>.
+
+#ifndef RUST_AST_LOWER_FORMAT_ARGS
+#define RUST_AST_LOWER_FORMAT_ARGS
+
+#include "rust-ast-full-decls.h"
+#include "rust-hir-full-decls.h"
+
+namespace Rust {
+namespace HIR {
+
+class FormatArgsLowering
+{
+public:
+  FormatArgsLowering ();
+  HIR::Expr *go (AST::FormatArgs &fmt);
+
+private:
+};
+
+} // namespace HIR
+} // namespace Rust
+
+#endif // ! RUST_AST_LOWER_FORMAT_ARGS
diff --git a/gcc/rust/resolve/rust-ast-resolve-base.cc b/gcc/rust/resolve/rust-ast-resolve-base.cc
index 04a0bb65ec2..5a9f54fc7f1 100644
--- a/gcc/rust/resolve/rust-ast-resolve-base.cc
+++ b/gcc/rust/resolve/rust-ast-resolve-base.cc
@@ -653,7 +653,8 @@ ResolverBase::visit (AST::FunctionParam &)
 void
 ResolverBase::visit (AST::FormatArgs &fmt)
 {
-  rust_sorry_at (0, "unimplemented format_args!() visitor");
+  rust_sorry_at (fmt.get_locus (), "%s:%u: unimplemented FormatArgs visitor",
+		 __FILE__, __LINE__);
 }
 
 } // namespace Resolver
