Returning trait objects without allocating on the heap

Howdy. Would it be considered useful for the compiler to support a form of Box that can live on the stack? I'm thinking that we'd be looking at a compiler-generated enum with variants for each implementation generated, and thus sized on the stack to the largest variant.

This could be useful when a function wants to return a trait object that can have multiple implementations. Something like:

trait MyTrait {}

struct MyStruct1;

impl MyTrait for MyStruct1 {}

struct MyStruct2;

impl MyTrait for MyStruct2 {}

fn my_func(flag: bool) -> dyn MyTrait {
    if flag {
        MyStruct1
    } else {
        MyStruct2
    }
}

My motivation for this question is based on wanting to avoid boxing a return type on the heap. Thanks for the dialogue.

Once the "unsized locals" feature lands, this will be automatically possible for all dynamically-sized types.

2 Likes

I haven’t looked into the feature for a while, but wasn’t that for unsized arguments rather than return values?

To be honest, I don't know. I wouldn't expect them to be any different in the first place.

The OP as phrased sounds more like ad-hoc sum types (perhaps in combination with RPIT + delegating dispatch) to me.

It's possible to write a type with an upper limit on the size and alignment of the inner type, and which can support any struct that fits.

3 Likes

There are crates that do something like this:

It would be really useful, it seems to me that using some return optimization like RVO would work perfectly, instead of allocating a Box on the heap it could reserve a space on the stack in the place where the trait object will be stored. I don't know, I'm not even a compiler expert.

There is an interest in such, but it's not trivial and movement has been slow.

Are you able to elaborate citing references to the interest? Thanks.

Thanks. I looked up unsized locals. It was specifically stated that returning unsized isn’t supported. unsized_locals - The Rust Unstable Book

I don't have a list handy, but e.g.

And probably other conversations in those forums/repos.

1 Like

dyn star seems to be designed for it: Rust Playground

#![feature(dyn_star)]
#![allow(incomplete_features)]

trait MyTrait {}

#[repr(transparent)] // needs to have the same ABI as a pointer
struct MyStruct1(usize);
impl MyTrait for MyStruct1 {}

#[repr(transparent)] // needs to have the same ABI as a pointer
struct MyStruct2(usize);
impl MyTrait for MyStruct2 {}

// compiles
fn my_func(flag: bool) -> dyn* MyTrait {
    if flag {
        MyStruct1(1)
    } else {
        MyStruct2(2)
    }
}

Update: materials about dyn star

3 Likes

Thanks for the pointer (pun intended)!

This seems as though it'd provide what I was looking for. I read that the constraint of the dyn pointer is that it has to be sized as per a pointer. I'm assuming that this would mean that if my structs had a u128, say, then they couldn't be returned this way without a box? That would make sense and somewhat avoids unexpected stack consumption.

Looking at the RFC

  1. The following expressions must always return a Sized type:
    1. Function calls, method calls, operator expressions
      • implementing unsized return values for function calls would require the called function to do the alloca in our stack frame.
        […]

And also directly in the unstable book entry you liked

    // You CANNOT have unsized return types.
    fn my_function() -> dyn Any { *answer() }  // ERROR

Yes. Because

To coerce a value of type T into a dyn* Trait, two constraints must be met:

  • The type T must be pointer-sized or smaller.
  • The type T must implement Trait
2 Likes

Probably of limited use then?

The dyn star type is fairly experimental: do not use it in production or any serious project now (use it for fun instead).


To answer OP

  • refactoring your code to aviod trait objects is the best way
  • use 3rd crates as listed above by someone: but be careful to unsoundness
    • actually I posted the same topic few months ago, How to have a stacked dyn Trait? that showed crates like smallbox and stack_dst(in 0.7.2, but 0.8.1 is ok!) have UB
1 Like

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.