&[&str] vs [&str; N] in Function Parameters

In Rust, handling function parameters often involves understanding the distinction between fixed-size arrays ([&str; N]) and slices (&[&str]). While both are collections of elements, their use cases and behaviours differ significantly, particularly when working with dynamically sized inputs.

This article will help clarify the differences and explain when to use each approach, complete with example code.

The Basics: What Are These Types?[&str; N] (Fixed-Size Arrays)

  • Represents an array with a fixed number of elements (N), each of type &str.
  • The size (N) is known at compile time and cannot change.

Example:

let fixed_args: [&str; 2] = ["arg1", "arg2"];

&[&str] (Slices)

  • Represents a reference to a dynamically sized array (slice) of elements, all of type &str.
  • The size is determined at runtime, making it flexible for handling variable-length inputs.

Example:

let dynamic_args: &[&str] = &["arg1", "arg2", "arg3"];

Function Parameters: When to Use Which?

Fixed-Size Arrays ([&str; N])

If your function requires a specific number of arguments, you can use a fixed-size array. This ensures the input always meets the expected size.

Example:

fn deploy_fixed(constructor_args: [&str; 2]) -> String {
    constructor_args.join(",")
}

fn main() {
    let result = deploy_fixed(["arg1", "arg2"]); // Must be exactly two elements
    println!("{}", result);
}

Pros:

  • Ensures the size of the input is correct at compile time.

Cons:

  • Inflexible for varying input sizes.

Slices (&[&str])

If your function needs to handle a variable number of arguments, slices are the idiomatic choice. They allow you to work with inputs of any size without taking ownership.

Example:

fn deploy_dynamic(constructor_args: &[&str]) -> String {
    constructor_args.join(",")
}

fn main() {
    let result = deploy_dynamic(&["arg1", "arg2", "arg3"]);
    println!("{}", result);
}

Pros:

  • Flexible for dynamic input sizes.
  • Matches idiomatic Rust patterns for function parameters.

Cons:

  • Does not enforce a specific size at compile time.

Comparing the Two Approaches

Feature[&str; N]&[&str]
SizeFixed at compile timeDynamic at runtime
FlexibilityLimitedHigh
Use CaseKnown, fixed inputVariable input
OwnershipTakes ownershipBorrows

Practical Example: Deploying with a Salt

Here’s a practical example using slices (&[&str]) to handle variable-length arguments for a function that simulates deploying a contract:

use sha2::{Sha256, Digest};

struct Deployer;

impl Deployer {
    /// Generates a 32-byte salt by hashing the input string.
    fn generate_salt(input: &str) -> [u8; 32] {
        let mut hasher = Sha256::new();
        hasher.update(input.as_bytes());
        let hash = hasher.finalize();
        let mut salt = [0u8; 32];
        salt.copy_from_slice(&hash);
        salt
    }

    /// Simulates deploying a contract using a wasm hash, salt, and arguments.
    fn deploy(wasm_hash: [u8; 32], salt: [u8; 32], constructor_args: &[&str]) -> String {
        let mut deploy_hasher = Sha256::new();
        deploy_hasher.update(wasm_hash);
        deploy_hasher.update(salt);
        deploy_hasher.update(constructor_args.join(",").as_bytes());
        let address = deploy_hasher.finalize();

        format!("{:x}", address)
    }
}

fn main() {
    let wasm_hash = [0u8; 32];
    let salt = Deployer::generate_salt("example");
    let constructor_args = &["arg1", "arg2", "arg3"];

    let result = Deployer::deploy(wasm_hash, salt, constructor_args);
    println!("Deployed contract address: {}", result);
}

Explanation

  • generate_salt: Generates a 32-byte array by hashing an input string.
  • deploy: Combines the wasm hash, salt, and joined constructor arguments to produce a unique contract address.
  • &[&str]: Allows the function to handle a dynamic number of constructor arguments.

Key Takeaways

  1. Use [&str; N] for fixed-size inputs when the size is known and consistent.
  2. Use &[&str] for variable-length inputs to write flexible, idiomatic Rust functions.
  3. Slices are commonly used in real-world scenarios due to their dynamic nature and borrowing capabilities.

You’ll need to use what the function asks for if you’re dealing with someone else’s function, but if you’re writing your own, consider the above examples and don’t be afraid to experiment!

Here’s the link to Rust Playground and the example I’ve used

Thanks for viewing!

&[&str]