Hone logo
Hone
Problems

Rust Macro Challenge: Flexible String Joining

This challenge asks you to create a Rust macro that mimics the functionality of String::join but with greater flexibility. Specifically, you'll implement a macro that can join a collection of items into a single String, using a specified separator, and optionally filtering out empty strings before joining.

Problem Description

Your task is to create a procedural macro, let's call it flexible_join, that takes a collection of items and a separator string. The macro should produce a String where the items are concatenated together, with the separator placed between them.

Key Requirements:

  1. Generic Input: The macro should accept any iterable collection (e.g., Vec, slice, array) of items that can be converted into strings.
  2. Separator: A separator string must be provided.
  3. Filtering (Optional): The macro should have an option to exclude any empty strings from the final joined output.
  4. String Conversion: Items in the collection that are not already String or &str should be convertible into String (e.g., i32, f64, custom types implementing Display).

Expected Behavior:

  • If no filtering is requested, all items are joined.
  • If filtering is requested, only non-empty strings are joined.
  • The separator should only appear between joined elements, not at the beginning or end.
  • If the collection is empty or contains only items that would be filtered out, an empty String should be returned.

Examples

Example 1: Basic Joining

let words = vec!["hello", "world", "rust"];
let separator = ", ";
let result = flexible_join!(words, separator);
// Expected Output: "hello, world, rust"

Example 2: Joining with Numbers and Filtering Empty Strings

let data = vec![Some("apple"), None, Some("banana"), Some(""), Some("cherry")];
let separator = " | ";
let result = flexible_join!(data, separator, filter_empty);
// Expected Output: "apple | banana | cherry"

Example 3: Joining Empty Collection

let empty_vec: Vec<String> = vec![];
let separator = "-";
let result = flexible_join!(empty_vec, separator);
// Expected Output: ""

Example 4: Joining with Mixed Types and Filtering

let mixed_data = vec!["one", 2, "", "three", 4.5];
let separator = " - ";
let result = flexible_join!(mixed_data, separator, filter_empty);
// Expected Output: "one - 2 - three - 4.5"

Constraints

  • The macro must be implemented as a procedural macro.
  • The macro should handle collections of up to 1,000 elements for testing purposes.
  • Performance should be reasonable; avoid excessive intermediate allocations if possible, but correctness and clarity are prioritized.
  • The macro should be compatible with Rust editions 2021 and later.

Notes

  • You will likely need to use the quote and syn crates for parsing and generating Rust code within your procedural macro.
  • Consider how to handle the filter_empty flag. This could be an optional argument or a specific keyword.
  • Think about how to convert items into Strings. The Display trait is your friend here.
  • The filter_empty option should only consider items that are empty strings after conversion. None values from Options, for example, should not be treated as empty strings to be filtered by this specific flag, unless they explicitly convert to an empty string.
Loading editor...
rust