diff --git a/toolkit/components/glean/bindings/private/TimingDistribution.cpp b/toolkit/components/glean/bindings/private/TimingDistribution.cpp
index 78aad6d4cd8efe8725a6733086c5671c6b92f03c..5a1a6d3daf2ae428ebf6e556eb58bf4ca003f3af 100644
--- a/toolkit/components/glean/bindings/private/TimingDistribution.cpp
+++ b/toolkit/components/glean/bindings/private/TimingDistribution.cpp
@@ -25,7 +25,7 @@ extern "C" NS_EXPORT void GIFFT_TimingDistributionStart(
   auto mirrorId = mozilla::glean::HistogramIdForMetric(aMetricId);
   if (mirrorId) {
     mozilla::glean::GetTimerIdToStartsLock().apply([&](auto& lock) {
-      auto tuple = std::make_tuple(aMetricId, aTimerId);
+      auto tuple = mozilla::glean::MetricTimerTuple{aMetricId, aTimerId};
       // It should be all but impossible for anyone to have already inserted
       // this timer for this metric given the monotonicity of timer ids.
       (void)NS_WARN_IF(lock.ref()->Remove(tuple));
@@ -40,7 +40,8 @@ extern "C" NS_EXPORT void GIFFT_TimingDistributionStopAndAccumulate(
   auto mirrorId = mozilla::glean::HistogramIdForMetric(aMetricId);
   if (mirrorId) {
     mozilla::glean::GetTimerIdToStartsLock().apply([&](auto& lock) {
-      auto optStart = lock.ref()->Extract(std::make_tuple(aMetricId, aTimerId));
+      auto tuple = mozilla::glean::MetricTimerTuple{aMetricId, aTimerId};
+      auto optStart = lock.ref()->Extract(tuple);
       // The timer might not be in the map to be removed if it's already been
       // cancelled or stop_and_accumulate'd.
       if (!NS_WARN_IF(!optStart)) {
@@ -67,8 +68,8 @@ extern "C" NS_EXPORT void GIFFT_TimingDistributionCancel(
     mozilla::glean::GetTimerIdToStartsLock().apply([&](auto& lock) {
       // The timer might not be in the map to be removed if it's already been
       // cancelled or stop_and_accumulate'd.
-      (void)NS_WARN_IF(
-          !lock.ref()->Remove(std::make_tuple(aMetricId, aTimerId)));
+      auto tuple = mozilla::glean::MetricTimerTuple{aMetricId, aTimerId};
+      (void)NS_WARN_IF(!lock.ref()->Remove(tuple));
     });
   }
 }
diff --git a/toolkit/components/glean/build_scripts/glean_parser_ext/templates/gifft.jinja2 b/toolkit/components/glean/build_scripts/glean_parser_ext/templates/gifft.jinja2
index 4a58ef541103e45b4ec6ee532dfc7a72811a61d0..9d0c10cdf6dc2fe913a17d41b20ce68e76846b8e 100644
--- a/toolkit/components/glean/build_scripts/glean_parser_ext/templates/gifft.jinja2
+++ b/toolkit/components/glean/build_scripts/glean_parser_ext/templates/gifft.jinja2
@@ -35,7 +35,10 @@ using Telemetry::{{ probe_type }}ID;
 
 using MetricId = uint32_t; // Same type as in api/src/private/mod.rs
 using TimerId = uint64_t; // Same as in TimingDistribution.h.
-using MetricTimerTuple = std::tuple<MetricId, TimerId>;
+struct MetricTimerTuple {
+  MetricId mMetricId;
+  TimerId mTimerId;
+};
 class MetricTimerTupleHashKey : public PLDHashEntryHdr {
  public:
   using KeyType = const MetricTimerTuple&;
@@ -49,15 +52,17 @@ class MetricTimerTupleHashKey : public PLDHashEntryHdr {
 
   KeyType GetKey() const { return mValue; }
   bool KeyEquals(KeyTypePointer aKey) const {
-    return std::get<0>(*aKey) == std::get<0>(mValue) && std::get<1>(*aKey) == std::get<1>(mValue);
+    return aKey->mMetricId == mValue.mMetricId &&
+           aKey->mTimerId == mValue.mTimerId;
   }
 
   static KeyTypePointer KeyToPointer(KeyType aKey) { return &aKey; }
   static PLDHashNumber HashKey(KeyTypePointer aKey) {
     // Chosen because this is how nsIntegralHashKey does it.
-    return HashGeneric(std::get<0>(*aKey), std::get<1>(*aKey));
+    return HashGeneric(aKey->mMetricId, aKey->mTimerId);
   }
   enum { ALLOW_MEMMOVE = true };
+  static_assert(std::is_trivially_copyable_v<MetricTimerTuple>);
 
  private:
   const MetricTimerTuple mValue;
@@ -170,6 +175,7 @@ class ScalarIDHashKey : public PLDHashEntryHdr {
     return static_cast<std::underlying_type<ScalarID>::type>(*aKey);
   }
   enum { ALLOW_MEMMOVE = true };
+  static_assert(std::is_trivially_copyable_v<ScalarID>);
 
  private:
   const ScalarID mValue;