On this page

Rust Server SDK

Statsig's Next-gen Rust Server SDK built in our [Server Core](/server-core) framework

Set up the SDK

  1. 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 version
    

    Or, you can use the cargo command:

    shell
    cargo add statsig-rust
    
    You can find the latest version and documentation at crates.io/crates/statsig-rust.
  2. 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 options parameter accepts a StatsigOptions object to customize the SDK.

    rust
    use 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?;
    
    initialize performs a network request. After initialize completes, 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 the SDK is initialized, 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:
rust
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.
rust
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.
rust
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.

jsx
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:

rust
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 just a boolean value. For additional metadata about the evaluation, use the Get Feature Gate API, which returns a FeatureGate object:

rust
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:

rust
// 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 lets a single Statsig instance be created and accessed globally throughout your application. This is useful when multiple parts of the codebase need Statsig without passing an instance around.

  • Statsig::new_shared(sdk_key, options): Creates a new shared instance of Statsig that can be accessed globally
  • Statsig::shared(): Returns the shared instance
  • Statsig::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 will result 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:

rust
result = statsig.check_gate_with_options(&user, 'a_gate_name', FeatureGateEvaluationOptions {disable_exposure_logging: true});
rust
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 to take advantage of advanced gate and config conditions (like country or OS/browser level checks) and to correctly measure the impact of experiments on metrics and events. As explained here, at least one identifier (userID or customID) is required to provide 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.

rust
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 ensure all logged events are flushed before shutdown, call shutdown() before your app or server shuts down:

rust
statsig.shutdown().await?;

Alternatively, you can manually flush events without shutting down:

rust
// 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 user
  • dynamic_config_evaluated - Fired when a dynamic config is retrieved for a user
  • experiment_evaluated - Fired when an experiment is evaluated for a user
  • layer_evaluated - Fired when a layer is evaluated for a user
  • specs_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_config object with values and metadata
  • Experiment Events include: the full experiment object with variant assignment and parameters
  • Layer Events include: the full layer object with allocated experiment and parameters
  • Specs Updated Events include source, source_api, and values metadata, where values.time is 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
rust
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.

Rust
// 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.

rust
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.

rust
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.

rust
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.
rust
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 our support team, your sales contact, or through our 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.

rust
// 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:

rust
// 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)

rust
// 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 our support team, your sales contact, or through our Slack community if you want this enabled.

Field Mapping

The fields returned by these methods correspond to the following user properties:

plaintext
// 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?