Re: [RFC] [GSoC]: STRBUF_INIT_CONST: initialize `strbuf` to constant string
- From
- Mateo Patino <mateopatinodev@gmail.com>
- Date
- Mar 28, 2026, 21:40 UTC
- Message-ID
- <CAFRsFoWRRnbrJdp_HVuoW-AEMqz_XjoP5yFAFP73VVN9nhdp2w@mail.gmail.com>
- In-Reply-To
- <CAPig+cQcLJxxtsH0OeSP2DVUbSg8x95B-7n18fK9BVTJVywEtQ@mail.gmail.com>
Show 12 quoted lines
> > But, having reread the threads which your initial email referenced, I > think the bigger issue is that we're dealing with an XY Problem[1]. > The original problem "X" being discussed was how to achieve static > initialization of some string variables while still allowing the > variables to be later pointed at heap-allocated memory, but at the > same time avoiding memory leaks when those reassignments occur. The > proposed solution "Y" was to somehow employ `strbuf` to solve X, > however, it turns out that `strbuf` is utterly unsuitable for this > use-case. Unfortunately, this "Y" proposal was then turned into a > GitHub issue[2] which has led to this email thread as well as those > aborted and misdirected submissions which you referenced earlier.
I didn't know this concept of an XY problem. It seems very useful to describe this kind of mistake in software development. I will keep it in mind from now on. Thanks for sharing it!
Show 32 quoted lines
>
> If we take a step back and focus on the original problem rather than
> focusing on how to twist strbuf into something it was never meant to
> be, then a potential solution becomes clearer. Let's restate the
> original problem:
>
> static const char *global_var = "thimble";
>
> void maybe_assign(const char **var, ...) {
> if (...some_condition...) {
> /* ??? free((void *)*var) ??? */
> *var = some_heap_allocated_str;
> }
> }
>
> maybe_assign(&global_var, ...);
> ...
> maybe_assign(&global_var, ...);
>
> When maybe_assign() is called, it doesn't know whether or not the
> incoming `var` points at a static string literal ("thimble") or at
> some heap-allocated string, so it doesn't know whether or not to first
> free() `var` before assigning the new value. To solve this, we need a
> flag which indicates whether the string stored in the variable needs
> to be freed before the variable is reassigned. So, this suggests a
> dedicated, simple structure and a few related functions and a macro or
> two. For instance, something like this:
>
> struct str {
> char *s;
> int free_me;
> };Thanks for explaining the original problem in such detail, I see I really hadn't completely understood what the original problem "X" was.
To clarify, you are imagining this `struct str` more as a "smart pointer" than a full string abstraction, correct? I was going to propose including a `size_t len` member for this struct, but after some thought, I feel like that would somewhat transform `struct str` into a string abstraction, which `strbuf` already is. The way you're imagining `struct str` could be used around in the Git codebase is as a wrapper whose only purpose is to inform clients of a string's ownership, correct?
Show 41 quoted lines
>
> /* initialize `str` from a literal string (i.e. "foo") */
> #define STR_INIT(X) { .s = (char *)(X), .free_me = 0 }
>
> void str_release(str *x) {
> if (x.free_me)
> FREE_AND_NULL(x.s);
> x.free_me = 0;
> }
>
> /* take ownership of a heap-allocated string */
> void str_take(str *x, char * s) {
> str_release(x);
> x.s = s;
> x.free_me = 1;
> }
>
> /* assign a string literal (i.e. "foo") */
> void str_assign(str *x, const char *s) {
> str_release(x);
> x.s = (char *)s;
> x.free_me = 0;
> }
>
> That's probably about all you need to solve the stated problem.
> Given the above, the original problem statement can be "fixed" by taking
> advantage of the above structure and functions:
>
> static struct str global_var = STR_INIT("thimble");
>
> void maybe_assign(str *var, ...) {
> if (...some_condition...)
> str_assign(var, some_heap_allocated_str);
> }
>
> maybe_assign(&global_var, ...);
>
> Clients which need the value simply access the `.s` member directly.
> And there is no need to have any functions to morph the string in any
> way. If a client needs that functionality, it is easy enough to create
> and populate a proper `strbuf` from the `.s` member.So if we were to make this into a patch, would we implement this as a local helper in config.c, where the original problem started? I imagine this small ownership interface could likely be used in multiple places around the codebase, so my first instinct would be to not restrict it to config.c. Would it be too premature to give this `struct str` its own module? If so, then how would an idea of this sort be first presented to the community as a patch?
Thanks again for the detailed explanations!
> [1]: https://xyproblem.info/ > [2]: https://github.com/gitgitgadget/git/issues/398