Can I serialize a function pointer with using Serde?

The following codes can not compile because that function pointer can not be serialized:

use serde::{Serialize, Deserialize};

#[derive(Serialize, Deserialize)]
struct MyStruct {
    f: fn(bool, bool) -> bool,
}

What can I do?

Not much. There's no practical/universal/meaningful way in which a function pointer can be serialized and then deserialized (on a potentially different machine).

Why are you trying to serialize a function pointer? What's your higher-level goal with this?

7 Likes

In this case, if you know the function has no side-effects, you can store a truth table instead:

#[derive(Debug,Copy,Clone,Serialize,Deserialize)]
pub struct TruthTable2(u8);

impl<F:Fn(bool,bool)->bool> From<F> for TruthTable2 {
    fn from(f:F)->Self {
        TruthTable2( (f(true,true) as u8) << 3
                   | (f(true,false) as u8) << 2
                   | (f(false,true) as u8) << 1
                   | f(false,false) as u8
        )
    }
}

impl TruthTable2 {
    pub fn apply(self, a:bool, b:bool)->bool {
        1 & (self.0 >> ((a as u8) << 1 | (b as u8))) == 1
    }
}
4 Likes

For example, I have a type as follow:

struct LongTimeToGet {
    part1: Sig,
    part2: Sig,
    func: fn(bool, bool) -> bool,
}

I have to calculate long time to get an instance of LongTimeToGet, so I have to serialize it to local file that I can use it in the next running time. Or I can give the saved file to another person who is using the same crate as mine, so he can also use the instance directly.

Yeah, I get that you want to serialize the data, but it doesn't explain why you want to serialize a function. What purpose does func serve? Why does a seemingly pure data type contain a function pointer? That's very unusual, and that's the part I am asking about.

1 Like

I thought it maybe a way to reduce the code amout.
The type LongTimeToGet implement trait MyTrait, and will be parsed to a function, where the type LongTimeToGet do something on its own. OutFn can not affect LongTimeToGet, it just call it:

impl MyTrait for LongTimeToGet {
    fn do_something() {}
}

fn OutFn(x: dyn MyTrait) {
    do things 
    x.do_something()
    do things
}

If I have four kinds of fn(bool, bool) -> like:

func_logic1(a: bool, b: bool) -> a && b
func_logic2(a: bool, b: bool) -> a || b
func_logic3(a: bool, b: bool) -> a && !b
func_logic4(a: bool, b: bool) -> !a && b

If I don't make function pointer as to be a LongTimeToGet's field, I have to define four different type of it:

struct LongTimeToGetLogic1 {
    part1: Sig,
    part2: Sig,
}
struct LongTimeToGetLogic2 {
    part1: Sig,
    part2: Sig,
}
struct LongTimeToGetLogic3 {
    part1: Sig,
    part2: Sig,
}
struct LongTimeToGetLogic4 {
    part1: Sig,
    part2: Sig,
}

impl MyTrait for LongTimeToGetLogic1 {
    fn do_something(&self) {
        self.part1 && self.part2
    }
}
impl MyTrait for LongTimeToGetLogic2 {
    fn do_something(&self) {
        self.part1 || self.part2
    }
}
impl MyTrait for LongTimeToGetLogic3 {
    fn do_something(&self) {
        self.part1 && !self.part2
    }
}
impl MyTrait for LongTimeToGetLogic4 {
    fn do_something(&self) {
        !self.part1 && self.part2
    }
}

But with function pointer, I can:

impl MyTrait for LongTimeToGet {
    fn do_something(&self) {
        self.f(self.part1, self.part2)
    }
}

To get a similar effect as the function pointer, you can use an enum. Roughly…

enum Logic {
    L1,
    L2,
    L3,
    L4,
}

struct LongTimeToGet {
    part1: Sig,
    part2: Sig,
    logic: Logic,
}

impl MyTrait for LongTimeToGet {
    fn do_something(&self) {
        match self.logic {
            Logic::L1 => self.part1 && self.part2,
            Logic::L2 => self.part1 || self.part2,
            Logic::L3 => self.part1 && !self.part2,
            Logic::L4 => !self.part1 && self.part2,
        }
    }
}

then the enum can derive Serialize.

6 Likes

In this case, it would probably be more principled to make the abstract behavior explicit using an enum or a generic parameter, and serialize this high-level description instead of trying to serialize raw machine code. Playground.

You can even go crazy and define an AST for arbitrary trees of Boolean operations, and evaluate it like an interpreter: Playground.

The point is, you have to create some abstract, platform-independent representation in order to stand a chance of serializing it.

1 Like