Custom UUID implementation on C++
You forgot to include &lt;string&gt;, and misspelt std::size_t (twice).
Since this doesn't create a RFC-4122-compliant UUID, I would change the name so it doesn't get used where it shouldn't.  Perhaps create_random_id() or similar?
I don't like the name x for the digit string - I'd prefer something more descriptive like hex_chars or even just hex.

We could consider constructing a string that's already long enough, then setting all its characters:
std::string uuid(len, '\0');
for (auto&amp; c: uuid) {
    c = x[dis(gen)];
}

Or perhaps replace the loop with a standard algorithm:
std::string uuid;
uuid.reserve(len);
std::generate_n(std::back_inserter(uuid), len,
                [&amp;]{ return x[dis(gen)]; });

Though I don't think that's necessarily clearer.

Going a little over the top, we could make a general select-from-collection function and use that:
template&lt;std::ranges::sized_range C, class G&gt;
auto sample_from(C const&amp; collection, G&amp;&amp; generator)
{
    std::uniform_int_distribution&lt;typename C::size_type&gt; dis{0, std::ranges::size(collection) - 1};
    return collection[dis(generator)];
}

⋮
    static const std::string_view hex_chars = &quot;0123456789abcdef&quot;;
    std::generate_n(std::back_inserter(uuid), len,
                    [&amp;]{ return sample_from(hex_chars, gen); });


We might even make a custom input-iterator for selecting random characters:
class random_char_source
{
    const std::string chars;
    std::mt19937 generator;
    std::uniform_int_distribution&lt;std::size_t&gt; distribution;

public:
    using iterator_category = std::input_iterator_tag;
    using value_type = char;
    using difference_type = int;
    using pointer = const char*;
    using reference = const char&amp;;

    explicit random_char_source(std::string chars, std::random_device::result_type seed = std::random_device{}())
        : chars{std::move(chars)},
          generator{seed},
          distribution{0, this-&gt;chars.size() - 1}
    {}

    // it's an input iterator
    char operator*() { return chars[distribution(generator)]; }
    random_char_source&amp; operator++() { return *this; }
};

That makes our ID generator very simple:
static std::string generate_random_id(std::size_t len)
{
    auto hex_chars = random_char_source{&quot;0123456789abcdef&quot;};

    std::string uuid;
    uuid.reserve(len);
    std::copy_n(hex_chars, len, std::back_inserter(uuid));
    return uuid;
}


However, for generating power-of-two numbers, and since we have a UniformRandomBitGenerator (the std::mt19937 object), we can extract bits directly:
static std::string random_id(std::size_t len)
{
    static const std::string_view hex_chars = &quot;0123456789abcdef&quot;;
    using Generator = std::mt19937;

    Generator gen{std::random_device{}()};

    std::string uuid;
    uuid.reserve(len);

    while (uuid.size() &lt; len) {
        auto n = gen();
        for (auto i = Generator::max();  i &amp; 0x8 &amp;&amp; uuid.size() &lt; len;  i &gt;&gt;= 4) {
            uuid += hex_chars[n &amp; 0xf];
            n &gt;&gt;= 4;
        }
    }

    return uuid;
}

You could probably make a simpler and more readable version of that.