Using a complex generic function as a struct attribute


#1

Hi there, I’m having some issues trying to use a complex function as an attribute in a struct. The function I want to have as an attribute is this:

fn the_fn<'a, S: Into<Cow<'a, str>>>(input: S) -> Cow<'a, str> {
    input.into() // Example, just to get the header
}

And I’ve thought of using a Box, but seems difficult to know the type of that attribute. Even if I change the input type to &str (which is possible in my use case) I get issues with lifetime parameters.

Here is the play code: https://is.gd/9Ked25

How can I do it properly?


#2

Unfortunately you can’t because generics don’t exist at runtime. We call this monomorphization. At compile time, rust “instantiates” a concrete copy of every generic function for every set of concrete type arguments used by the program. For example, given:

fn debug<A: Debug>(a: A) {
    println!("{:?}", &a);
}

fn main() {
    debug(1u32);
    debug("my string");
}

Rust will instantiate debug::<u32> and debug::<&'static str> but the compiled program won’t know anything about, e.g., debug::<String>.

This is why rust has dynamic dispatch (trait objects, e.g., &Debug). The following program will only instantiate a single debug function.

fn debug(a: &Debug) {
    println!("{:?}", a);
}

fn main() {
    debug(&1u32);
    debug(&"my string");
}

However, that won’t work in your case because:

  1. Into requires that all types on which it is implemented be Sized. Sized is a marker trait implemented on types whose size is known at compile time. Into imposes this constraint because Into::into takes self by-value (consumes it) and, in today’s rust, there’s no way to take a “dynamically” (known only at runtime) sized type (DST) by value.
  2. Traits bounded by Sized cannot be turned into trait objects because the size of a trait object is not known at compile time.

My explanation of dynamically sized/statically sized types is very light on details so please say so if you’re confused.


#3

OK, I get it, I will use Fn(&str) -> String+ Send + Sync, since it’s OK in my current situation. Thanks for the explanation!