How to call a method that consumes self on a boxed trait object?





.everyoneloves__top-leaderboard:empty,.everyoneloves__mid-leaderboard:empty,.everyoneloves__bot-mid-leaderboard:empty{ height:90px;width:728px;box-sizing:border-box;
}







7















I have the following sketch of an implementation:



trait Listener {
fn some_action(&mut self);
fn commit(self);
}

struct FooListener {}

impl Listener for FooListener {
fn some_action(&mut self) {
println!("{:?}", "Action!!");
}

fn commit(self) {
println!("{:?}", "Commit");
}
}

struct Transaction {
listeners: Vec<Box<Listener>>,
}

impl Transaction {
fn commit(self) {
// How would I consume the listeners and call commit() on each of them?
}
}

fn listener() {
let transaction = Transaction {
listeners: vec![Box::new(FooListener {})],
};
transaction.commit();
}


I can have Transactions with listeners on them that will call the listener when something happens on that transaction. Since Listener is a trait, I store a Vec<Box<Listener>>.



I'm having a hard time implementing commit for Transaction. Somehow I have to consume the boxes by calling commit on each of the stored Listeners, but I can't move stuff out of a box as far as I know.



How would I consume my listeners on commit?










share|improve this question

























  • Moving "stuff" out of a box is easy; you just dereference it. Your case is more complicated because you no longer know how big the value stored inside the box was. This means you get an error: cannot move a value of type Listener: the size of Listener cannot be statically determined.

    – Shepmaster
    Oct 7 '17 at 13:44




















7















I have the following sketch of an implementation:



trait Listener {
fn some_action(&mut self);
fn commit(self);
}

struct FooListener {}

impl Listener for FooListener {
fn some_action(&mut self) {
println!("{:?}", "Action!!");
}

fn commit(self) {
println!("{:?}", "Commit");
}
}

struct Transaction {
listeners: Vec<Box<Listener>>,
}

impl Transaction {
fn commit(self) {
// How would I consume the listeners and call commit() on each of them?
}
}

fn listener() {
let transaction = Transaction {
listeners: vec![Box::new(FooListener {})],
};
transaction.commit();
}


I can have Transactions with listeners on them that will call the listener when something happens on that transaction. Since Listener is a trait, I store a Vec<Box<Listener>>.



I'm having a hard time implementing commit for Transaction. Somehow I have to consume the boxes by calling commit on each of the stored Listeners, but I can't move stuff out of a box as far as I know.



How would I consume my listeners on commit?










share|improve this question

























  • Moving "stuff" out of a box is easy; you just dereference it. Your case is more complicated because you no longer know how big the value stored inside the box was. This means you get an error: cannot move a value of type Listener: the size of Listener cannot be statically determined.

    – Shepmaster
    Oct 7 '17 at 13:44
















7












7








7


2






I have the following sketch of an implementation:



trait Listener {
fn some_action(&mut self);
fn commit(self);
}

struct FooListener {}

impl Listener for FooListener {
fn some_action(&mut self) {
println!("{:?}", "Action!!");
}

fn commit(self) {
println!("{:?}", "Commit");
}
}

struct Transaction {
listeners: Vec<Box<Listener>>,
}

impl Transaction {
fn commit(self) {
// How would I consume the listeners and call commit() on each of them?
}
}

fn listener() {
let transaction = Transaction {
listeners: vec![Box::new(FooListener {})],
};
transaction.commit();
}


I can have Transactions with listeners on them that will call the listener when something happens on that transaction. Since Listener is a trait, I store a Vec<Box<Listener>>.



I'm having a hard time implementing commit for Transaction. Somehow I have to consume the boxes by calling commit on each of the stored Listeners, but I can't move stuff out of a box as far as I know.



How would I consume my listeners on commit?










share|improve this question
















I have the following sketch of an implementation:



trait Listener {
fn some_action(&mut self);
fn commit(self);
}

struct FooListener {}

impl Listener for FooListener {
fn some_action(&mut self) {
println!("{:?}", "Action!!");
}

fn commit(self) {
println!("{:?}", "Commit");
}
}

struct Transaction {
listeners: Vec<Box<Listener>>,
}

impl Transaction {
fn commit(self) {
// How would I consume the listeners and call commit() on each of them?
}
}

fn listener() {
let transaction = Transaction {
listeners: vec![Box::new(FooListener {})],
};
transaction.commit();
}


I can have Transactions with listeners on them that will call the listener when something happens on that transaction. Since Listener is a trait, I store a Vec<Box<Listener>>.



I'm having a hard time implementing commit for Transaction. Somehow I have to consume the boxes by calling commit on each of the stored Listeners, but I can't move stuff out of a box as far as I know.



How would I consume my listeners on commit?







rust ownership-semantics






share|improve this question















share|improve this question













share|improve this question




share|improve this question








edited Oct 7 '17 at 13:37









Shepmaster

161k16332476




161k16332476










asked Oct 7 '17 at 13:35









WorldSEnderWorldSEnder

2,95711740




2,95711740













  • Moving "stuff" out of a box is easy; you just dereference it. Your case is more complicated because you no longer know how big the value stored inside the box was. This means you get an error: cannot move a value of type Listener: the size of Listener cannot be statically determined.

    – Shepmaster
    Oct 7 '17 at 13:44





















  • Moving "stuff" out of a box is easy; you just dereference it. Your case is more complicated because you no longer know how big the value stored inside the box was. This means you get an error: cannot move a value of type Listener: the size of Listener cannot be statically determined.

    – Shepmaster
    Oct 7 '17 at 13:44



















Moving "stuff" out of a box is easy; you just dereference it. Your case is more complicated because you no longer know how big the value stored inside the box was. This means you get an error: cannot move a value of type Listener: the size of Listener cannot be statically determined.

– Shepmaster
Oct 7 '17 at 13:44







Moving "stuff" out of a box is easy; you just dereference it. Your case is more complicated because you no longer know how big the value stored inside the box was. This means you get an error: cannot move a value of type Listener: the size of Listener cannot be statically determined.

– Shepmaster
Oct 7 '17 at 13:44














1 Answer
1






active

oldest

votes


















8














Applying commit to the boxed object is not allowed because the trait object doesn't know its size (and it's not constant at compile-time). Since you plan to use listeners as boxed objects, what you can do is acknowledge that commit will be invoked on the box and change its signature accordingly:



trait Listener {
fn some_action(&mut self);
fn commit(self: Box<Self>);
}

struct FooListener {}

impl Listener for FooListener {
fn some_action(&mut self) {
println!("{:?}", "Action!!");
}

fn commit(self: Box<Self>) {
println!("{:?}", "Commit");
}
}


This enables Transaction to compile as you wrote it, because inside the implementation of FooListener the size of Self is well known and it is perfectly possible to move the object out of the box and consume both.



The price of this solution is that Listener::commit now requires a Box. If that is not acceptable, you could declare both commit(self) and commit_boxed(self: Box<Self>) in the trait, requiring all types to implement both, possibly using private functions or macros to avoid code duplication. This is not very elegant, but it would satisfy both the boxed and unboxed use case without loss of performance.






share|improve this answer


























  • Does this work for trait objects? Like, if I have a let box: Box<Listener>, can I call box.commit();?

    – Lucretiel
    Jul 9 '18 at 6:17











  • I ask because Self is going to be FooListener, not Listener, correct?

    – Lucretiel
    Jul 9 '18 at 6:17











  • @Lucretiel Yes, the answer (and likely the question) was written with trait objects in mind.

    – user4815162342
    Jul 9 '18 at 6:20












Your Answer






StackExchange.ifUsing("editor", function () {
StackExchange.using("externalEditor", function () {
StackExchange.using("snippets", function () {
StackExchange.snippets.init();
});
});
}, "code-snippets");

StackExchange.ready(function() {
var channelOptions = {
tags: "".split(" "),
id: "1"
};
initTagRenderer("".split(" "), "".split(" "), channelOptions);

StackExchange.using("externalEditor", function() {
// Have to fire editor after snippets, if snippets enabled
if (StackExchange.settings.snippets.snippetsEnabled) {
StackExchange.using("snippets", function() {
createEditor();
});
}
else {
createEditor();
}
});

function createEditor() {
StackExchange.prepareEditor({
heartbeatType: 'answer',
autoActivateHeartbeat: false,
convertImagesToLinks: true,
noModals: true,
showLowRepImageUploadWarning: true,
reputationToPostImages: 10,
bindNavPrevention: true,
postfix: "",
imageUploader: {
brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
allowUrls: true
},
onDemand: true,
discardSelector: ".discard-answer"
,immediatelyShowMarkdownHelp:true
});


}
});














draft saved

draft discarded


















StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f46620790%2fhow-to-call-a-method-that-consumes-self-on-a-boxed-trait-object%23new-answer', 'question_page');
}
);

Post as a guest















Required, but never shown

























1 Answer
1






active

oldest

votes








1 Answer
1






active

oldest

votes









active

oldest

votes






active

oldest

votes









8














Applying commit to the boxed object is not allowed because the trait object doesn't know its size (and it's not constant at compile-time). Since you plan to use listeners as boxed objects, what you can do is acknowledge that commit will be invoked on the box and change its signature accordingly:



trait Listener {
fn some_action(&mut self);
fn commit(self: Box<Self>);
}

struct FooListener {}

impl Listener for FooListener {
fn some_action(&mut self) {
println!("{:?}", "Action!!");
}

fn commit(self: Box<Self>) {
println!("{:?}", "Commit");
}
}


This enables Transaction to compile as you wrote it, because inside the implementation of FooListener the size of Self is well known and it is perfectly possible to move the object out of the box and consume both.



The price of this solution is that Listener::commit now requires a Box. If that is not acceptable, you could declare both commit(self) and commit_boxed(self: Box<Self>) in the trait, requiring all types to implement both, possibly using private functions or macros to avoid code duplication. This is not very elegant, but it would satisfy both the boxed and unboxed use case without loss of performance.






share|improve this answer


























  • Does this work for trait objects? Like, if I have a let box: Box<Listener>, can I call box.commit();?

    – Lucretiel
    Jul 9 '18 at 6:17











  • I ask because Self is going to be FooListener, not Listener, correct?

    – Lucretiel
    Jul 9 '18 at 6:17











  • @Lucretiel Yes, the answer (and likely the question) was written with trait objects in mind.

    – user4815162342
    Jul 9 '18 at 6:20
















8














Applying commit to the boxed object is not allowed because the trait object doesn't know its size (and it's not constant at compile-time). Since you plan to use listeners as boxed objects, what you can do is acknowledge that commit will be invoked on the box and change its signature accordingly:



trait Listener {
fn some_action(&mut self);
fn commit(self: Box<Self>);
}

struct FooListener {}

impl Listener for FooListener {
fn some_action(&mut self) {
println!("{:?}", "Action!!");
}

fn commit(self: Box<Self>) {
println!("{:?}", "Commit");
}
}


This enables Transaction to compile as you wrote it, because inside the implementation of FooListener the size of Self is well known and it is perfectly possible to move the object out of the box and consume both.



The price of this solution is that Listener::commit now requires a Box. If that is not acceptable, you could declare both commit(self) and commit_boxed(self: Box<Self>) in the trait, requiring all types to implement both, possibly using private functions or macros to avoid code duplication. This is not very elegant, but it would satisfy both the boxed and unboxed use case without loss of performance.






share|improve this answer


























  • Does this work for trait objects? Like, if I have a let box: Box<Listener>, can I call box.commit();?

    – Lucretiel
    Jul 9 '18 at 6:17











  • I ask because Self is going to be FooListener, not Listener, correct?

    – Lucretiel
    Jul 9 '18 at 6:17











  • @Lucretiel Yes, the answer (and likely the question) was written with trait objects in mind.

    – user4815162342
    Jul 9 '18 at 6:20














8












8








8







Applying commit to the boxed object is not allowed because the trait object doesn't know its size (and it's not constant at compile-time). Since you plan to use listeners as boxed objects, what you can do is acknowledge that commit will be invoked on the box and change its signature accordingly:



trait Listener {
fn some_action(&mut self);
fn commit(self: Box<Self>);
}

struct FooListener {}

impl Listener for FooListener {
fn some_action(&mut self) {
println!("{:?}", "Action!!");
}

fn commit(self: Box<Self>) {
println!("{:?}", "Commit");
}
}


This enables Transaction to compile as you wrote it, because inside the implementation of FooListener the size of Self is well known and it is perfectly possible to move the object out of the box and consume both.



The price of this solution is that Listener::commit now requires a Box. If that is not acceptable, you could declare both commit(self) and commit_boxed(self: Box<Self>) in the trait, requiring all types to implement both, possibly using private functions or macros to avoid code duplication. This is not very elegant, but it would satisfy both the boxed and unboxed use case without loss of performance.






share|improve this answer















Applying commit to the boxed object is not allowed because the trait object doesn't know its size (and it's not constant at compile-time). Since you plan to use listeners as boxed objects, what you can do is acknowledge that commit will be invoked on the box and change its signature accordingly:



trait Listener {
fn some_action(&mut self);
fn commit(self: Box<Self>);
}

struct FooListener {}

impl Listener for FooListener {
fn some_action(&mut self) {
println!("{:?}", "Action!!");
}

fn commit(self: Box<Self>) {
println!("{:?}", "Commit");
}
}


This enables Transaction to compile as you wrote it, because inside the implementation of FooListener the size of Self is well known and it is perfectly possible to move the object out of the box and consume both.



The price of this solution is that Listener::commit now requires a Box. If that is not acceptable, you could declare both commit(self) and commit_boxed(self: Box<Self>) in the trait, requiring all types to implement both, possibly using private functions or macros to avoid code duplication. This is not very elegant, but it would satisfy both the boxed and unboxed use case without loss of performance.







share|improve this answer














share|improve this answer



share|improve this answer








edited Oct 9 '17 at 19:01

























answered Oct 7 '17 at 22:37









user4815162342user4815162342

64.6k595151




64.6k595151













  • Does this work for trait objects? Like, if I have a let box: Box<Listener>, can I call box.commit();?

    – Lucretiel
    Jul 9 '18 at 6:17











  • I ask because Self is going to be FooListener, not Listener, correct?

    – Lucretiel
    Jul 9 '18 at 6:17











  • @Lucretiel Yes, the answer (and likely the question) was written with trait objects in mind.

    – user4815162342
    Jul 9 '18 at 6:20



















  • Does this work for trait objects? Like, if I have a let box: Box<Listener>, can I call box.commit();?

    – Lucretiel
    Jul 9 '18 at 6:17











  • I ask because Self is going to be FooListener, not Listener, correct?

    – Lucretiel
    Jul 9 '18 at 6:17











  • @Lucretiel Yes, the answer (and likely the question) was written with trait objects in mind.

    – user4815162342
    Jul 9 '18 at 6:20

















Does this work for trait objects? Like, if I have a let box: Box<Listener>, can I call box.commit();?

– Lucretiel
Jul 9 '18 at 6:17





Does this work for trait objects? Like, if I have a let box: Box<Listener>, can I call box.commit();?

– Lucretiel
Jul 9 '18 at 6:17













I ask because Self is going to be FooListener, not Listener, correct?

– Lucretiel
Jul 9 '18 at 6:17





I ask because Self is going to be FooListener, not Listener, correct?

– Lucretiel
Jul 9 '18 at 6:17













@Lucretiel Yes, the answer (and likely the question) was written with trait objects in mind.

– user4815162342
Jul 9 '18 at 6:20





@Lucretiel Yes, the answer (and likely the question) was written with trait objects in mind.

– user4815162342
Jul 9 '18 at 6:20




















draft saved

draft discarded




















































Thanks for contributing an answer to Stack Overflow!


  • Please be sure to answer the question. Provide details and share your research!

But avoid



  • Asking for help, clarification, or responding to other answers.

  • Making statements based on opinion; back them up with references or personal experience.


To learn more, see our tips on writing great answers.




draft saved


draft discarded














StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f46620790%2fhow-to-call-a-method-that-consumes-self-on-a-boxed-trait-object%23new-answer', 'question_page');
}
);

Post as a guest















Required, but never shown





















































Required, but never shown














Required, but never shown












Required, but never shown







Required, but never shown

































Required, but never shown














Required, but never shown












Required, but never shown







Required, but never shown







Popular posts from this blog

Can a sorcerer learn a 5th-level spell early by creating spell slots using the Font of Magic feature?

Does disintegrating a polymorphed enemy still kill it after the 2018 errata?

A Topological Invariant for $pi_3(U(n))$