Call trait method without borrowing or moving the object

I have a list of trait objects. I'd like to call a method defined by the implementer of the trait, but instead of passing the object as self, I want to pass the list that contains the object. I think the way to do that would be to somehow use the trait object to resolve and call the method, but without moving or borrowing it during the call. And I think the only way to do that (without nightly+arbitrary_self_types+raw pointers) is a static method. Here's what I mean in code:

trait Foo {
    fn bar(all: &mut [Box<dyn Foo>]);
}

struct S;

impl Foo for S {
    fn bar(all: &mut [Box<dyn Foo>]) {
        println!("{}", all.len());
    }
}

fn main() {
    let mut all: Vec<Box<dyn Foo>> = vec![Box::new(S)];
    all[0].bar(&mut all);
}

(Playground)

This doesn't compile because the trait is not object-safe, which rustc tells me is because it can't build a vtable. But there's no reason rust couldn't theoretically put a static method in the vtable, right? It's just a function pointer. I can prove that the compiler has enough information to resolve the method by tweaking the trait:

trait Foo {
    fn bar(&self) -> fn(&mut [Box<dyn Foo>]);
}

struct S;

impl Foo for S {
    fn bar(&self) -> fn(&mut [Box<dyn Foo>]) {
        |all| {
            println!("{}", all.len());
        }
    }
}

fn main() {
    let mut all: Vec<Box<dyn Foo>> = vec![Box::new(S)];
    all[0].bar()(&mut all);
}

(Playground)

But that will get ugly fast once the trait grows beyond a single one-arg method.

I might be barking up the wrong tree here. Is there a cleaner way to do this?

The thing you're asking for is ill-defined. [Box<dyn Foo>] doesn't have “the” vtable, it is a slice of many separate dyn-pointers each of which has its own vtable that may be different. The whole point of putting dyn Foo in a collection is to allow every element to be a different implementing type.

In your all[0].bar()(&mut all); you're picking the vtable of the first element to supply the bar operation. This might be a reasonable operation for your particular purposes, but it's not something the compiler will automatically do for you, because it's making a particular choice that is likely wrong for other situations where a [Box<dyn Trait>] comes up. (Why should it be the first element? Maybe it should be the last element, or maybe the operation should be called only on spans of the same type.)


This doesn't mean that there is no way to write this code better, but it would help if you described what you want this operation on list for — what problem you're actually trying to solve. Then we can try to suggest clean solutions for that problem, without assuming the particular implementation elements “there must be a trait method on dyn Foo that takes a list of dyn Foos”.

For example, maybe it makes sense for Foo to be a trait implemented by containers, not individual elements. That way there would be only one vtable and no dispatch problem, but it'd introduce other complications. We need more context.

The issue is not putting in the function pointer to the vtable. The issue is getting it out. If you don't have a value (i.e., &self), only a type, then where do you get the vtable from?

I did specifically want the first element in that example code. I could also choose a particular element by index, or iterate over the entire list, passing each element the mutable list. The more general idea is a loosely ECS-ish system, with behavior defined by trait objects instead of systems. Does that make sense or should I try to write an expanded example?

I do have a value (&self) to access the vtable. The problem isn't getting the function pointer out. The problem is that because &self is a borrowed reference, it forces you to borrow. afaik all method receiver types force a borrow. Calling a method with &self forces you to do these two operations simultaneously:

  1. Borrow the trait object
  2. Execute the function

Instead of doing them simultaneously, I would like to do #1 to get the function pointer, then do #2 after #1.


Maybe my first example was misleading. I don't need a static method per se. I need a trait object to access its owner, something like this:

trait Foo {
    fn bar(&self, all: &mut [Box<dyn Foo>], index: usize);
}

struct S;

impl Foo for S {
    fn bar(&self, all: &mut [Box<dyn Foo>], index: usize) {
        println!("I am #{}/{}", index, all.len());
    }
}

fn main() {
    let mut all: Vec<Box<dyn Foo>> = vec![Box::new(S), Box::new(S)];
    // Choose some index into the list according to game logic
    let index = 1;
    all[index].bar(&mut all, index);
}

On the last line, the borrow of all[index] conflicts with &mut all. I want to somehow terminate the borrow of all[index] before calling the method, which means not passing &self to the trait method.

This seems like a reasonable concept to me. Looks like you essentially want to split the dispatching from the method call itself, and the borrow of the trait object used to dispatch should end before the call starts.

Here's a way I can think of to make this work.

trait Foo {
    fn bar(&self) -> fn(all: &mut [Box<dyn Foo>], index: usize);
}

struct S;

impl Foo for S {
    fn bar(&self) -> fn(all: &mut [Box<dyn Foo>], index: usize) {
        |all, index| {
            println!("I am #{}/{}", index, all.len());
        }
    }
}

fn main() {
    let mut all: Vec<Box<dyn Foo>> = vec![Box::new(S), Box::new(S)];
    // Choose some index into the list according to game logic
    let index = 1;
    all[index].bar()(&mut all, index);
}

For convenience, to improve the experience of implementing such a method, you could add another trait and a generic implementation like this:

trait Foo: DynFoo {
    fn bar(all: &mut [Box<dyn Foo>], index: usize)
    where
        Self: Sized;
}

trait DynFoo {
    fn bar(&self) -> fn(all: &mut [Box<dyn Foo>], index: usize);
}

impl<T: Foo> DynFoo for T {
    fn bar(&self) -> fn(all: &mut [Box<dyn Foo>], index: usize) {
        <Self as Foo>::bar
    }
}

struct S;

impl Foo for S {
    fn bar(all: &mut [Box<dyn Foo>], index: usize) { // no more closure syntax necessary
        println!("I am #{}/{}", index, all.len());
    }
}

fn main() {
    let mut all: Vec<Box<dyn Foo>> = vec![Box::new(S), Box::new(S)];
    // Choose some index into the list according to game logic
    let index = 1;
    all[index].bar()(&mut all, index);
}
6 Likes

Another way to do this would be to use the unstable feature arbitrary_self_types which allows self: *const Self on object-safe trait methods. If you ignore the pointed-to value (i.e. never dereference the pointer), this is a way to rid the dispatchable self-object from any active borrow. The advantage is a slight performance advantage, since the alternative but stable approach presented above would do two dynamic function calls in a row, one for calling the method that returns another function pointer, and one to call that function pointer itself. The disadvantage is of course that it’s using unstable nightly-only features. Here's a code example

#![feature(arbitrary_self_types)]

trait Foo {
    // safe-to-call method, hence effectively the pointer's target cannot actually be used
    fn bar(self: *const Self, all: &mut [Box<dyn Foo>], index: usize);
}

struct S;

impl Foo for S {
    fn bar(self: *const Self, all: &mut [Box<dyn Foo>], index: usize) {
        println!("I am #{}/{}", index, all.len());
    }
}

fn main() {
    let mut all: Vec<Box<dyn Foo>> = vec![Box::new(S), Box::new(S)];
    // Choose some index into the list according to game logic
    let index = 1;
    Foo::bar(&*all[index], &mut all, index);
}
1 Like

That's a nice trait trick. The only downsides are the two extra parentheses and maybe a little runtime cost. Thanks for the idea!

The nightly+arbitrary_self_types+raw pointers is a good idea too but I'd rather stay on stable