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;
}
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 Transaction
s 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 Listener
s, 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
add a comment |
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 Transaction
s 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 Listener
s, 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
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
add a comment |
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 Transaction
s 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 Listener
s, 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
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 Transaction
s 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 Listener
s, 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
rust ownership-semantics
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
add a comment |
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
add a comment |
1 Answer
1
active
oldest
votes
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.
Does this work for trait objects? Like, if I have alet box: Box<Listener>
, can I callbox.commit();
?
– Lucretiel
Jul 9 '18 at 6:17
I ask becauseSelf
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
add a comment |
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
});
}
});
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
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
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.
Does this work for trait objects? Like, if I have alet box: Box<Listener>
, can I callbox.commit();
?
– Lucretiel
Jul 9 '18 at 6:17
I ask becauseSelf
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
add a comment |
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.
Does this work for trait objects? Like, if I have alet box: Box<Listener>
, can I callbox.commit();
?
– Lucretiel
Jul 9 '18 at 6:17
I ask becauseSelf
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
add a comment |
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.
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.
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 alet box: Box<Listener>
, can I callbox.commit();
?
– Lucretiel
Jul 9 '18 at 6:17
I ask becauseSelf
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
add a comment |
Does this work for trait objects? Like, if I have alet box: Box<Listener>
, can I callbox.commit();
?
– Lucretiel
Jul 9 '18 at 6:17
I ask becauseSelf
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
add a comment |
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.
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
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
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
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
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