diff --git a/CHANGELOG.md b/CHANGELOG.md
index 73e13418d..5ed6ace96 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -2,6 +2,9 @@
[Full changelog](https://github.com/mozilla/glean/compare/v33.8.0...main)
+* Rust
+ * Introduce the String List metric type in the RLB. [#1380](https://github.com/mozilla/glean/pull/1380)
+
# v33.8.0 (2020-12-10)
[Full changelog](https://github.com/mozilla/glean/compare/v33.7.0...v33.8.0)
diff --git a/docs/user/metrics/string_list.md b/docs/user/metrics/string_list.md
index ac6535c59..e9a99dc71 100644
--- a/docs/user/metrics/string_list.md
+++ b/docs/user/metrics/string_list.md
@@ -150,6 +150,43 @@ Assert.Equal(
```
+
+
+```Rust
+use glean_metrics;
+
+// Add them one at a time
+engines.iter().for_each(|x|
+ search::engines.add(x)
+);
+
+// Set them in one go
+search::engines.set(engines)
+```
+
+There are test APIs available too:
+
+```Rust
+use glean::ErrorType;
+use glean_metrics;
+
+// Was anything recorded?
+assert!(search::engines.test_get_value(None).is_some());
+// Does it have the expected value?
+// IMPORTANT: It may have been truncated -- see "Limits" below
+assert_eq!(
+ vec!["Google".to_string(), "DuckDuckGo".to_string()],
+ search::engines.test_get_value(None)
+);
+// Were any of the values too long, and thus an error was recorded?
+assert_eq!(
+ 0,
+ search::engines.test_get_num_recorded_errors(ErrorType::InvalidValue)
+);
+```
+
+
+
{{#include ../../tab_footer.md}}
## Limits
@@ -172,3 +209,4 @@ Assert.Equal(
* [Kotlin API docs](../../../javadoc/glean/mozilla.telemetry.glean.private/-string-list-metric-type/index.html)
* [Python API docs](../../../python/glean/metrics/string_list.html)
+* [Rust API docs](../../../docs/glean/private/struct.StringListMetric.html)
diff --git a/glean-core/rlb/src/private/mod.rs b/glean-core/rlb/src/private/mod.rs
index a422c5d7e..8975f89f3 100644
--- a/glean-core/rlb/src/private/mod.rs
+++ b/glean-core/rlb/src/private/mod.rs
@@ -13,6 +13,7 @@ mod ping;
mod quantity;
mod recorded_experiment_data;
mod string;
+mod string_list;
mod timespan;
mod uuid;
@@ -26,4 +27,5 @@ pub use ping::PingType;
pub use quantity::QuantityMetric;
pub use recorded_experiment_data::RecordedExperimentData;
pub use string::StringMetric;
+pub use string_list::StringListMetric;
pub use timespan::TimespanMetric;
diff --git a/glean-core/rlb/src/private/string_list.rs b/glean-core/rlb/src/private/string_list.rs
new file mode 100644
index 000000000..179d554ce
--- /dev/null
+++ b/glean-core/rlb/src/private/string_list.rs
@@ -0,0 +1,108 @@
+// This Source Code Form is subject to the terms of the Mozilla Public
+// License, v. 2.0. If a copy of the MPL was not distributed with this
+// file, You can obtain one at https://mozilla.org/MPL/2.0/.
+
+use inherent::inherent;
+use std::sync::Arc;
+
+use glean_core::metrics::MetricType;
+use glean_core::ErrorType;
+
+use crate::dispatcher;
+
+// We need to wrap the glean-core type: otherwise if we try to implement
+// the trait for the metric in `glean_core::metrics` we hit error[E0117]:
+// only traits defined in the current crate can be implemented for arbitrary
+// types.
+
+/// This implements the developer-facing API for recording string list metrics.
+///
+/// Instances of this class type are automatically generated by the parsers
+/// at build time, allowing developers to record values that were previously
+/// registered in the metrics.yaml file.
+#[derive(Clone)]
+pub struct StringListMetric(pub(crate) Arc);
+
+impl StringListMetric {
+ /// The public constructor used by automatically generated metrics.
+ pub fn new(meta: glean_core::CommonMetricData) -> Self {
+ Self(Arc::new(glean_core::metrics::StringListMetric::new(meta)))
+ }
+}
+
+#[inherent(pub)]
+impl glean_core::traits::StringList for StringListMetric {
+ fn add>(&self, value: S) {
+ let metric = Arc::clone(&self.0);
+ let new_value = value.into();
+ dispatcher::launch(move || crate::with_glean(|glean| metric.add(glean, new_value)));
+ }
+
+ fn set(&self, value: Vec) {
+ let metric = Arc::clone(&self.0);
+ dispatcher::launch(move || crate::with_glean(|glean| metric.set(glean, value)));
+ }
+
+ fn test_get_value<'a, S: Into