Rust Server SDK
Statsig's Next-gen Rust Server SDK built in our Server Core framework
Set up the SDK
Install the SDK
To use the SDK, add the Statsig Rust package to your Cargo.toml file:
toml[dependencies] statsig-rust = "X.Y.Z" # Replace with the latest versionOr, you can use the cargo command:
shellcargo add statsig-rustYou can find the latest version and documentation at crates.io/crates/statsig-rust.
Initialize the SDK
After installation, initialize the SDK using a Server Secret Key from the Statsig console.
Keep Server Secret Keys private. If you expose one, you can disable and recreate it in the Statsig console.
An optional
optionsparameter accepts a StatsigOptions object to customize the SDK.rustuse statsig_rust::{Statsig, StatsigOptions}; use std::sync::Arc; // Simple initialization let statsig = Statsig::new("server-secret-key", None); statsig.initialize().await?; // Or with StatsigOptions let mut options = StatsigOptions::default(); options.environment = Some("development".to_string()); let statsig = Statsig::new("server-secret-key", Some(Arc::new(options))); statsig.initialize().await?; // Don't forget to shutdown when done statsig.shutdown().await?;initializeperforms a network request. Afterinitializecompletes, virtually all SDK operations are synchronous (refer to Evaluating Feature Gates in the Statsig SDK). The SDK fetches updates from Statsig in the background independently of API calls.
Working with the SDK
Checking a feature flag/gate
After you initialize the SDK, you can fetch a Feature Gate. Feature Gates create logic branches in code that you can roll out to different users from the Statsig Console. Gates are always CLOSED or OFF (equivalent to return false;) by default.
All APIs require you to specify the user (refer to Statsig user) associated with the request. For example:
use statsig_rust::{Statsig, StatsigUserBuilder};
let user = StatsigUserBuilder::new_with_user_id("a-user".to_string()).build();
if statsig.check_gate(&user, "a_gate") {
// Gate is on, enable new feature
} else {
// Gate is off
}
Reading a dynamic config
Feature Gates are useful for simple on/off switches with optional user targeting. To send a different set of values (strings, numbers, and similar types) to clients based on user attributes such as country, use Dynamic Configs. The API is similar to Feature Gates, but returns a full JSON object configurable on the server from which you can fetch typed parameters.
use statsig_rust::{Statsig, StatsigUserBuilder, DynamicConfigEvaluationOptions};
use std::sync::Arc;
// Get a dynamic config for a specific user
let user = StatsigUserBuilder::new_with_user_id("my_user".to_string()).build();
let config = statsig.get_dynamic_config(&user, "a_config");
// Access config values with type-safe getters and fallback values
let product_name = config.get_string("product_name", "Awesome Product v1"); // returns String
let price = config.get_double("price", 10.0); // returns f64
let should_discount = config.get_bool("discount", false); // returns bool
let quantity = config.get_int("quantity", 1); // returns i64
// Advanced Usage:
// You can disable exposure logging for this specific check
let mut options = DynamicConfigEvaluationOptions::default();
options.disable_exposure_logging = Some(true);
let config = statsig.get_dynamic_config_with_options(&user, "a_config", &options);
// The config object also provides metadata about the evaluation
println!("{}", config.rule_id); // The ID of the rule that served this config
println!("{}", config.id_type); // The type of the evaluation (experiment, config, etc)
Getting a layer/experiment
Use Layers/Experiments to run A/B/n experiments. Two APIs are available. Statsig recommends Layers because they make parameters reusable and support mutually exclusive experiments.
use statsig_rust::{Statsig, StatsigUserBuilder};
// Values via get_layer
let user = StatsigUserBuilder::new_with_user_id("my_user".to_string()).build();
let layer = statsig.get_layer(&user, "user_promo_experiments");
let title = layer.get_string("title", "Welcome to Statsig!");
let discount = layer.get_double("discount", 0.1);
// Via get_experiment
let title_exp = statsig.get_experiment(&user, "new_user_promo_title");
let price_exp = statsig.get_experiment(&user, "new_user_promo_price");
let title = title_exp.get_string("title", "Welcome to Statsig!");
let discount = price_exp.get_double("discount", 0.1);
Parameter stores
Use Parameter Stores when you want to define a parameter without deciding whether it should be a Feature Gate, Experiment, or Dynamic Config. Parameter Stores let you change the parameter type at any point in the Statsig console without a new deployment. Parameter Stores are optional, but parameterizing your application provides future flexibility and allows non-technical Statsig users to turn parameters into experiments.
let param_store = statsig.get_parameter_store("my_parameters");
let param_store_value = param_store.get(&user, "my_parameter_value", false); //false is fallback value
println!("param_store_value: {}", param_store_value);
Logging an event
To track custom events, call the Log Event API. Specify the user, event name, and an optional value or metadata object:
use statsig_rust::{Statsig, StatsigUserBuilder};
use std::collections::HashMap;
use crate::evaluation::dynamic_value::DynamicValue;
// Create a user
let user = StatsigUserBuilder::new_with_user_id("user_id".to_string()).build();
// Create metadata hashmap
let mut metadata = HashMap::new();
metadata.insert("price".to_string(), "9.99".into());
metadata.insert("item_name".to_string(), "diet_coke_48_pack".into());
// Log the event
statsig.log_event(
&user,
"add_to_cart",
Some("SKU_12345".into()), // value as DynamicValue
Some(metadata)
);
Learn more about identifying users, group analytics, and best practices for logging events in the logging events guide.
Retrieving feature gate metadata
In certain scenarios, you may need more information about a gate evaluation than a boolean value. For additional metadata about the evaluation, use the Get Feature Gate API, which returns a FeatureGate object:
use statsig_rust::{Statsig, StatsigUserBuilder};
// Create a user
let user = StatsigUserBuilder::new_with_user_id("user_id".to_string()).build();
// Get a feature gate
let gate = statsig.get_feature_gate(&user, "example_gate");
// Access gate properties
println!("{}", gate.rule_id);
println!("{}", gate.value); // Boolean value of the gate
Using shared instance
To create a single Statsig instance accessible globally throughout your codebase, use the shared instance functionality, which provides a singleton pattern:
// Create a shared instance that can be accessed globally
let statsig = Statsig::new_shared("server-secret-key", None).unwrap();
statsig.initialize().await?;
// Access the shared instance from anywhere in your code
let shared_statsig = Statsig::shared();
let is_feature_enabled = shared_statsig.check_gate(&user, "feature_name");
// Check if a shared instance exists
if Statsig::has_shared_instance() {
// Use the shared instance
}
// Remove the shared instance when no longer needed
Statsig::remove_shared();
The shared instance helps when multiple parts of your codebase need Statsig without passing an instance between them.
Statsig::new_shared(sdk_key, options): Creates a new shared instance of Statsig that you can access globallyStatsig::shared(): Returns the shared instanceStatsig::has_shared_instance(): Checks if a shared instance exists (useful when the shared instance may not be ready yet)Statsig::remove_shared(): Removes the shared instance (useful when you want to switch to a new shared instance)
has_shared_instance() and remove_shared() are helpful in specific scenarios but aren't required in most use cases where the shared instance is set up near the top of your application.
Also note that only one shared instance can exist at a time. Attempting to create a second shared instance results in an error.
Manual exposures
By default, the SDK automatically logs an exposure event when you check a gate, get a config, get an experiment, or call get() on a parameter in a layer. To delay exposure logging (for example, to log only after the user actually uses the feature), use manual exposures.
All main SDK functions (check_gate, get_dynamic_config, get_experiment, get_layer) accept an options parameter with a disable_exposure_logging field. When set to true, the SDK doesn't automatically log an exposure. You can then log the exposure manually at a later time:
result = statsig.check_gate_with_options(&user, 'a_gate_name', FeatureGateEvaluationOptions {disable_exposure_logging: true});
statsig.manually_log_gate_exposure(&user, 'a_gate_name')
Statsig user
The StatsigUser object represents a user in Statsig. You must provide a userID or at least one of the customIDs to identify the user.
When calling APIs that require a user, pass as much information as possible. Complete user information lets you take advantage of advanced gate and config conditions (like country or OS/browser-level checks) and correctly measure the impact of experiments on metrics and events. As explained in why an ID is always required for server SDKs, you must provide at least one identifier (userID or customID) to ensure a consistent experience for a given user.
In addition to userID, the top-level fields on StatsigUser are: email, ip, userAgent, country, locale, and appVersion. You can also pass any key-value pairs in an object or dictionary to the custom field and create targeting based on them.
Private attributes
Private attributes are user attributes that Statsig uses for evaluation but doesn't forward to any integrations. They are useful for PII or sensitive data that you don't want to send to third-party services.
use statsig_rust::StatsigUserBuilder;
use std::collections::HashMap;
// Create a user with just a user ID
let user = StatsigUserBuilder::new_with_user_id("user-123".to_string())
.build();
// Or create a user with custom IDs
let mut custom_ids = HashMap::new();
custom_ids.insert("employee_id".to_string(), "emp-456".to_string());
let user_with_custom_ids = StatsigUserBuilder::new_with_custom_ids(custom_ids)
.build();
// Create a user with several properties
let mut custom_fields = HashMap::new();
custom_fields.insert("plan".to_string(), "premium".into());
custom_fields.insert("age".to_string(), 25.into());
let user = StatsigUserBuilder::new_with_user_id("user-123".to_string())
.email(Some("user@example.com".to_string()))
.ip(Some("192.168.1.1".to_string()))
.user_agent(Some("Mozilla/5.0...".to_string()))
.country(Some("US".to_string()))
.locale(Some("en-US".to_string()))
.app_version(Some("1.0.0".to_string()))
.custom(Some(custom_fields))
.build();
// Private Attributes (not forwarded to integrations)
let mut private_attrs = HashMap::new();
private_attrs.insert("internal_id".to_string(), "emp-123".into());
let user_with_private = StatsigUserBuilder::new_with_user_id("user-123".to_string())
.email(Some("user@example.com".to_string()))
.private_attributes(Some(private_attrs))
.build();
Statsig options
You can pass an optional options parameter in addition to sdkKey during initialization to customize the Statsig client.
Proxy and custom network routing
Use proxy_config if your service needs a standard outbound HTTP proxy. If you need to route config sync through Statsig Forward Proxy or another custom source, use spec_adapters_config or specs_url instead. Set ca_cert_path when your environment requires a custom PEM CA bundle for outbound TLS.
Shutting Statsig down
Statsig batches and periodically flushes events. To flush all logged events before shutdown, call shutdown() before your app or server shuts down:
statsig.shutdown().await?;
Alternatively, you can manually flush events without shutting down:
// Manually flush events to the server
statsig.flush_events().await;
SDK event subscriptions
The Statsig SDK provides an event subscription system that lets you listen for evaluation events and lifecycle events in real time. This is useful for debugging, analytics, custom logging, and integration with external systems.
Supported events
The SDK supports subscribing to the following evaluation events:
gate_evaluated- Fired when a feature gate is evaluated for a userdynamic_config_evaluated- Fired when a dynamic config is retrieved for a userexperiment_evaluated- Fired when an experiment is evaluated for a userlayer_evaluated- Fired when a layer is evaluated for a userspecs_updated- Fired when the SDK updates its cached specs, including where the specs were loaded from"*"- Subscribe to all evaluation events
SDK event data
Each event includes relevant context about the evaluation:
- Gate Evaluated Events include:
gate_name,value(boolean),rule_id,reason - Dynamic Config Events include: the full
dynamic_configobject with values and metadata - Experiment Events include: the full
experimentobject with variant assignment and parameters - Layer Events include: the full
layerobject with allocated experiment and parameters - Specs Updated Events include
source,source_api, andvaluesmetadata, wherevalues.timeis the timestamp of the last update to the project in the Statsig console
Use cases
Event subscriptions are particularly useful for:
- Debugging: Monitor which features are being evaluated and their results
- Analytics: Track feature usage patterns and user segments
- Custom Logging: Send evaluation data to your own logging systems
- Integration: Forward events to external analytics or monitoring tools
- Testing: Verify that features are being evaluated as expected
Best practices
- Clean up subscriptions: Always unsubscribe when you no longer need to listen for events to prevent memory leaks
- Handle event data carefully: Event objects may contain sensitive user information depending on your configuration
- Use specific event types: Subscribe to specific events rather than "*" when possible for better performance
- Avoid heavy processing: Keep event handlers lightweight to avoid impacting SDK performance
use statsig_rust::{Statsig, StatsigUserBuilder, sdk_event_emitter::SdkEvent};
let statsig = Statsig::new("server-secret-key", None)?;
statsig.initialize().await?;
// Subscribe to gate evaluation events
let gate_sub_id = statsig.subscribe(SdkEvent::GATE_EVALUATED, |event| {
if let SdkEvent::GateEvaluated { gate_name, value, rule_id, reason } = event {
println!("Gate evaluated: {} = {}, rule: {}, reason: {}",
gate_name, value, rule_id, reason);
}
});
// Subscribe to dynamic config evaluation events
let config_sub_id = statsig.subscribe(SdkEvent::DYNAMIC_CONFIG_EVALUATED, |event| {
if let SdkEvent::DynamicConfigEvaluated { dynamic_config } = event {
println!("Config evaluated: {}", dynamic_config.name);
}
});
// Subscribe to experiment evaluation events
let experiment_sub_id = statsig.subscribe(SdkEvent::EXPERIMENT_EVALUATED, |event| {
if let SdkEvent::ExperimentEvaluated { experiment } = event {
println!("Experiment evaluated: {} -> {}",
experiment.name, experiment.group_name);
}
});
// Subscribe to layer evaluation events
let layer_sub_id = statsig.subscribe(SdkEvent::LAYER_EVALUATED, |event| {
if let SdkEvent::LayerEvaluated { layer } = event {
println!("Layer evaluated: {}", layer.name);
}
});
let specs_updated_sub_id = statsig.subscribe(SdkEvent::SPECS_UPDATED, |event| {
if let SdkEvent::SpecsUpdated {
source,
source_api,
values,
} = event
{
println!(
"Specs updated from {} via {} (project last edited at {})",
source, source_api, values.time
);
}
});
// Subscribe to all events
let all_events_sub_id = statsig.subscribe(SdkEvent::ALL, |event| {
println!("Event received: {}", event.get_name());
});
// Unsubscribe from specific event types
statsig.unsubscribe(SdkEvent::GATE_EVALUATED);
statsig.unsubscribe(SdkEvent::SPECS_UPDATED);
// Unsubscribe using subscription ID
statsig.unsubscribe_by_id(&config_sub_id);
// Unsubscribe from all events
statsig.unsubscribe_all();
Local overrides
Local Overrides let you override the values of gates, configs, experiments, and layers for testing. This is useful for local development or testing when you want to force a specific value without changing the configuration in the Statsig console.
// Overrides the given gate to the specified value
statsig.override_gate("test_gate", true, None);
// Overrides the given dynamic config to the provided value
statsig.override_dynamic_config("test_1", my_map.clone(), None); //my_map is HashMap<String, Value>
// Overrides the given experiment to the provided value
statsig.override_experiment("test_xp_1", my_map.clone(), None); //my_map is HashMap<String, Value>
// Overrides the given experiment to a particular groupname, available for experiments only
statsig.override_experiment_by_group_name("test_xp_1", "a_group_name", None);
// Overrides the given layer to the provided value
statsig.override_layer("user_promo_experiments", my_map.clone(), None); //my_map is HashMap<String, Value>
//Alternatively, get the Experiment object for a given groupName
let group_exp = statsig.get_experiment_by_group_name("pricing_experiment", "premium_group");
let premium_price = group_exp.get_double("price", 9.99);
Persistent storage
The Persistent Storage interface lets you implement custom storage for user-specific configurations. This enables you to persist user assignments across sessions, ensuring consistent experiment groups when a user returns. This is useful for client-side A/B testing where users must always receive the same variant.
pub trait PersistentStorageTrait: Send + Sync {
fn load(&self, key: &str) -> Result<Option<UserPersistedValues>, PersistentStorageErrorEnum>;
fn save(&self, key: &str, config_name: &str, data: &StickyValues) -> Result<(), PersistentStorageErrorEnum>;
fn delete(&self, key: &str, config_name: &str) -> Result<(), PersistentStorageErrorEnum>;
}
Data store
The Data Store interface lets you implement custom storage for Statsig configurations, enabling advanced caching strategies and integration with your preferred storage systems.
pub trait DataStoreTrait: Send + Sync {
fn initialize(&self) -> Result<(), DataStoreErrorEnum>;
fn shutdown(&self) -> Result<(), DataStoreErrorEnum>;
fn get(&self, key: &str) -> Result<Option<DataStoreResponse>, DataStoreErrorEnum>;
fn set(&self, key: &str, value: &str, time: Option<u64>) -> Result<(), DataStoreErrorEnum>;
fn support_polling_updates_for(&self, key: &str) -> Result<bool, DataStoreErrorEnum>;
}
pub struct DataStoreResponse {
pub result: String,
pub time: Option<u64>,
}
Custom output logger
The Output Logger interface lets you customize how the SDK logs messages, enabling integration with your own logging system and control over log verbosity.
pub trait OutputLogProvider: Send + Sync {
fn initialize(&self);
fn debug(&self, tag: &str, msg: String);
fn info(&self, tag: &str, msg: String);
fn warn(&self, tag: &str, msg: String);
fn error(&self, tag: &str, msg: String);
fn shutdown(&self);
}
Observability client
The Observability Client interface lets you monitor the health of the SDK by integrating with your own observability systems. This enables you to track metrics, errors, and performance data. For more information on the metrics emitted by Statsig SDKs, refer to the Monitoring documentation.
pub trait ObservabilityClient: Send + Sync {
fn init(&self);
fn increment(&self, metric_name: &str, value: f64, tags: &HashMap<String, String>);
fn gauge(&self, metric_name: &str, value: f64, tags: &HashMap<String, String>);
fn dist(&self, metric_name: &str, value: f64, tags: &HashMap<String, String>);
fn error(&self, tag: &str, error: &str);
}
Fields needed methods (Enterprise only)
This is available for Enterprise contracts. Reach out to the Statsig support team, your sales contact, or through the Slack community if you want this enabled.
These methods return an array of strings representing the user fields referenced in the targeting rules or conditions of the specified feature. Use them to understand which user properties influence a particular feature's behavior.
// Get fields needed for a gate
let fields_needed: Vec<String> = statsig.get_fields_needed_for_gate("gate_name");
// Get fields needed for a dynamic config
let fields_needed: Vec<String> = statsig.get_fields_needed_for_dynamic_config("config_name");
// Get fields needed for an experiment
let fields_needed: Vec<String> = statsig.get_fields_needed_for_experiment("experiment_name");
// Get fields needed for a layer
let fields_needed: Vec<String> = statsig.get_fields_needed_for_layer("layer_name");
Field mapping
The fields returned by these methods correspond to the following user properties:
// Field mapping between user properties and internal field names
let field_mapping = std::collections::HashMap::from([
("userID", "u"),
("email", "e"),
("ip", "i"),
("userAgent", "ua"),
("country", "c"),
("locale", "l"),
("appVersion", "a"),
("time", "t"),
("stableID", "s"),
("environment", "en"),
("targetApp", "ta"),
]);
// Custom fields are prefixed with "cf:"
// Example: fields.add("cf:" + field_name);
FAQ
Reference
Fields needed methods (Enterprise only)
// Get user fields needed for a gate evaluation
pub fn get_fields_needed_for_gate(&self, gate_name: &str) -> Vec<String>
// Get user fields needed for a dynamic config evaluation
pub fn get_fields_needed_for_dynamic_config(&self, config_name: &str) -> Vec<String>
// Get user fields needed for an experiment evaluation
pub fn get_fields_needed_for_experiment(&self, experiment_name: &str) -> Vec<String>
// Get user fields needed for a layer evaluation
pub fn get_fields_needed_for_layer(&self, layer_name: &str) -> Vec<String>
This is available for Enterprise contracts. Reach out to the Statsig support team, your sales contact, or through the Slack community if you want this enabled.
Field mapping
The fields returned by these methods correspond to the following user properties:
// Field mapping between user properties and internal field names
const fieldMapping = {
userID: 'u',
email: 'e',
ip: 'i',
userAgent: 'ua',
country: 'c',
locale: 'l',
appVersion: 'a',
time: 't',
stableID: 's',
environment: 'en',
targetApp: 'ta',
};
// Custom fields are prefixed with "cf:"
// Example: fields.add('cf:' + field);
Was this helpful?