When building a cdylib crate with stable-i686-pc-windows-msvc toolchain (rustc 1.44.1) the return value of an extern function is a random number and not i8 as expected.
i8 is 1 byte (signed).
c_uint is 4 bytes (unsigned).
16646152 = 0xFE0008: the 8 is there (+garbage from the stack).
The result is correct, it's just the types don't match.
You can make Rust match Python by returning a u32 instead. The same thing would happen if the parameter you pass is over 0xffff for the same reason.
None , integers, bytes objects and (unicode) strings are the only native Python objects that can directly be used as parameters in these function calls .. Python integers are passed as the platforms default C int type, their value is masked to fit into the C type.
Although, your original example printed 8 for me, so I couldn't test if setting the integer return type in Python would help. According to this table, c_byte still corresponds to Python's int.
Edit: I tested your original example with a debug build of the .DLL. When I tested with a release build, I was able to reproduce the random value. Setting balloon.restype = ctypes.c_byte printed 8 on the release build, so try that out.