2

This is what I'm trying to do:

fn foo() -> &'static str {
  let x = "Jeff";
  return &format!("Hello, {}", x);
}

I understand that it's not possible, since format!() makes a String in heap, while the result of foo is expected to be in static memory. Is there a similar macro/function, which would work like this:

fn foo() -> &'static str {
  let x = "Jeff";
  return static_format!("Hello, {...}", x);
}

Here, {...} is a five-bytes placeholder for world. The macro static_format! should allocate Hello, {...} in static, fill it up with arguments, and return it. Or maybe something else is possible, to simplify the process of formatting-and-returning static strings?

yegor256
  • 102,010
  • 123
  • 446
  • 597
  • 1
    Does this answer your question? [How to concatenate a literal string with a const string?](https://stackoverflow.com/questions/39023559/how-to-concatenate-a-literal-string-with-a-const-string) – PitaJ Jan 23 '22 at 18:11
  • 2
    I believe this crate: https://crates.io/crates/const_format is what you're looking for. – SirDarius Jan 23 '22 at 18:12
  • @SirDarius not really. `const_format` expects all arguments of `formatcp!()` to be `const`, which makes it not very useful in my case – yegor256 Jan 23 '22 at 18:26
  • Frame challenge: Why does your function return `&'static str` when it's dynamically creating the memory? That's simply not an accurate return type for that function. – Silvio Mayolo Jan 23 '22 at 18:33
  • @SilvioMayolo I have to return `&'static str` because that's what is expected in the `struct` I'm creating and returning. I can't use `String`, since it'll be dead by the moment the struct is returned. – yegor256 Jan 23 '22 at 19:23
  • @yegor256 How many times will you call `foo()`? Is it a problem for you that, if you manage to create a `&'static str`, it will never be freed? – user4815162342 Jan 23 '22 at 19:27
  • A `String` will *not* be dead the moment it returns. A `String` (without a `&` in front of it) will be owned by the caller, so it's up to *them* to decide how long it lasts. You're handing them full ownership, so you as the function no longer have any say over how long it lasts. – Silvio Mayolo Jan 23 '22 at 21:23

4 Answers4

3

I read your question and some of the comments as @sirdarius suggested you can use const_format for fancy formatting or simply use concat! from the standard library.

You mention that const_format requires everything to be const and that's not suitable for your case. Unfortunately, that's not possible. If you don't know the shape of the thing at compile time you cannot use it at compile time because it simply does not exist yet. Adding another str to the end of &str requires heap allocation due to the unknown size of the the runtime str you are planning to use.

I hope this made compile time vs runtime a little more clearer for you

nikoss
  • 3,254
  • 2
  • 26
  • 40
2

The only way to satisfy the signature of foo() and return a dynamic string is by leaking it:

fn foo() -> &'static str {
  let x = "Jeff";
  return Box::leak(format!("Hello, {}", x).into_boxed_str());
}

Caveat emptor: the return &'static str will never be freed, so this is only usable in cases where foo() is known to be called a fixed and small number of times, such as at program startup.

user4815162342
  • 141,790
  • 18
  • 296
  • 355
2

Depending on your use case, once_cell may be what you are looking for. It provides the generic Lazy<T> type that can be used to lazily initialize global data. Since the data is initialized at runtime, said initialization can do things that are impossible with compile time initialization.

Your function could be written like this:

use once_cell::sync::Lazy;

fn foo() -> &'static str {
    static VALUE: Lazy<String> = Lazy::new(|| {
        let x = "Jeff";
        format!("Hello, {}", x)
    });
  
    &VALUE
}

or, if the name can be a static too, you can move it outside of the function:

use once_cell::sync::Lazy;

static NAME: &str = "Jeff";

fn foo() -> &'static str {
    static VALUE: Lazy<String> = Lazy::new(|| {
        format!("Hello, {}", NAME)
    });
  
    &VALUE
}
Niklas Mohrin
  • 1,756
  • 7
  • 23
1

Try https://docs.rs/static_str_ops/latest/static_str_ops/#, which provides static_format!, static_concat! and staticize_once! for staticize strings + call_once semantic:

static_concat!(s1: expr, s2: expr, ...) -> &'static str

static_format!(s: expr, ...) -> &'static str

staticize_once!(expr: expr) -> &'static str
sighingnow
  • 791
  • 5
  • 11