Reputation: 2025
I know, the answers for similar questions already exist... kinda... all that I found were very high level instructions rather than code examples and I couldn't really follow them and I've been stuck in place for days now...
I need to share a variable with a closure. So I have my MockSlave
struct. I want it to create a vector that can then be shared with the shell
closure. The current version looks something like this
impl MockSlave {
pub async fn new(port: i32) -> Result<Self, Box<dyn std::error::Error>> {
let addr = String::from(format!("https://127.0.0.1:{}", port).as_str());
let endpoint = Endpoint::from_str(addr.as_str())?;
let commands = Rc::new(RefCell::new(Vec::new()));
let client = SlaveClient::new(endpoint, CommandProcessors {
shell: Some(|command| {
commands.borrow_mut().push(command);
Ok(())
})
}).await?;
Ok(MockSlave {
client,
commands
})
}
For clarity, here's SlaveClient::new
pub async fn new(endpoint: Endpoint, processors: CommandProcessors) -> Result<Self, tonic::transport::Error> {
let slave = SlaveClient {
client: SlaveManagerClient::connect(endpoint).await?,
processors
};
Ok(slave)
}
And here's CommandProcessors:
pub struct ReadWriteStreamingCommand {
pub command: Command
}
pub type ReadWriteSteamingCommandprocessor = fn(Box<ReadWriteStreamingCommand>) -> Result<(), Box<dyn Error>>;
pub struct CommandProcessors {
pub shell: Option<ReadWriteSteamingCommandprocessor>
}
Now, the fun part is that VSCode and cargo build
give me slightly different errors about the same thing.
VSCode says:
closures can only be coerced to `fn` types if they do not capture any variables
Meanwhile, cargo build
says
error[E0308]: mismatched types
--> tests/api_tests/src/mock_slave.rs:20:25
|
20 | shell: Some(|command| {
| _________________________^
21 | | commands.borrow_mut().push(command);
22 | | Ok(())
23 | | })
| |_____________^ expected fn pointer, found closure
Please help. This is at least my 10th approach to this problem over the course of a week. I am not going to move forward by myself...
Upvotes: 1
Views: 1318
Reputation: 70257
fn(Box<ReadWriteStreamingCommand>) -> Result<(), Box<dyn Error>>;
This is not the type you think it is. This is the type of pointers to functions that exist statically in your code. That is, functions defined at the top-level or closures which don't actually close over any variables. To accept general Rust callables (including traditional functions and closures), you need one of Fn
, FnMut
, or FnOnce
.
I can't tell from the small code you've shown which of the three you need, but the basic idea is this.
Use FnOnce
if you are only going to call the function at most once. This is useful if you're writing some kind of "control flow" type of construct or mapping on the inside of an Option
(where at most one value exists).
Use FnMut
if you are going to call the function several times but never concurrently. This is what's used by most Rust built-in collection functions like map
and filter
, since it's always called in a single thread. This is the one I find myself using the most often for general-purpose things.
Use Fn
if you need concurrent access and are planning to share across threads.
Every Fn
is an FnMut
(any function that can be called concurrently can be called sequentially), and every FnMut
is an FnOnce
(any function that can be called can obviously be called once), so you should choose the highest one on the list above that you can handle.
I'll assume you want FnMut
. If you decide one of the others is better, the syntax is the same, but you'll just need to change the name. The type you want, then, is
dyn FnMut(Box<ReadWriteStreamingCommand>) -> Result<(), Box<dyn Error>>
But this is a trait object and hence is not Sized
, so you can't do much with it. In particular, you definitely can't put it in another Sized
data structure. Instead, we'll wrap it in a Box
.
pub type ReadWriteSteamingCommandprocessor =
Box<dyn FnMut(Box<ReadWriteStreamingCommand>) -> Result<(), Box<dyn Error>>>;
And then
shell: Some(Box::new(|command| {
commands.borrow_mut().push(command);
Ok(())
}))
Upvotes: 3