Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can I create a list of owned trait objects without allocating each item on the heap separately?

I want an owned list of Rust trait objects. I could implement it as Vec<Box<dyn Trait>> but that allocates space on the heap for every trait object. What I’d prefer is a CompactList<dyn Trait> type with a memory representation that looks like:

[vtable1, size1, data1, vtable2, size2, data2, vtable3, size3, data3]

size* is the size in bytes of the corresponding data*.

With this, I could create an Iterator<Item = &dyn Trait>. The only operations I need on CompactList<T> are push() and iter().

like image 333
Calebmer Avatar asked Mar 18 '20 14:03

Calebmer


People also ask

What is dynamic dispatch rust?

Dynamic dispatch. Rust provides dynamic dispatch through a feature called 'trait objects'. Trait objects, like &Foo or Box<Foo> , are normal values that store a value of any type that implements the given trait, where the precise type can only be known at runtime.

What are trait objects rust?

A trait object is an opaque value of another type that implements a set of traits. The set of traits is made up of an object safe base trait plus any number of auto traits. Trait objects implement the base trait, its auto traits, and any supertraits of the base trait.

What is object safety in Rust?

Object Safety A trait is object safe if it has the following qualities (defined in RFC 255): All supertraits must also be object safe. Sized must not be a supertrait. In other words, it must not require Self: Sized . It must not have any associated constants.

What is dyn rust?

dyn is a prefix of a trait object's type. The dyn keyword is used to highlight that calls to methods on the associated Trait are dynamically dispatched. To use the trait this way, it must be 'object safe'. Unlike generic parameters or impl Trait , the compiler does not know the concrete type that is being passed.


1 Answers

The dynstack crate does what you want. It relies on the representation of fat pointers, which is what trait objects are, and that representation could theoretically change some day.

While it solves the problem of avoiding heap allocations for each object, its in-memory representation is different: Instead of a flat list, there are basically two lists:

  • [data1, data2, ...]
  • [(vtable1, size1), (vtable2, size2), ...]

Since the data structs can have different sizes, your representation doesn't support O(1) random access, while this one does. See this blog post for details.

Example, adapted from the documentation:

use dynstack::{dyn_push, DynStack};
use std::fmt::Debug;

let mut stack = DynStack::<dyn Debug>::new();
dyn_push!(stack, "hello, world!");
dyn_push!(stack, 0usize);
dyn_push!(stack, [1, 2, 3, 4, 5, 6]);

for item in stack.iter() {
    println!("{:?}", item);
}
like image 89
nnnmmm Avatar answered Oct 13 '22 16:10

nnnmmm