Hiroyuki Katsura
2019-Jun-05 06:33 UTC
[Libguestfs] The way of implementing structs of Rust bindings
Hi, I'm now implementing generators of Rust, and I faced a problem. In order for the wrapper to 'understand' the struct passed from C API, it is required to incorporate the definition of the struct in C into Rust code. I have two approaches. 1. Create raw struct(#[repr(C)]), which has the equivalent memory mapping to C struct and access through this struct in Rust 2. Use bindgen to create ffi struct from guestfs.h Each of them has advantages and disadvantages. # 1st approach This is highly dependent on the implementation of API because it is subject to the memory mapping of API struct. When the way struct is structured changes in API, you have to modify Rust bindings. In order to avoid this situation, you can define the same struct in C and Rust and then access the API struct in C and then Rust accesses the data through this struct. This means that ``` API: struct guestfs_A: - field: x FString C: struct RawA { char* x }; struct RawA to_RawA(struct guestfs_A* src) { struct RawA x = {src->x}; return x; } Rust: #repr(C) use std::ffi::CStr; use std::str; #[repr(C)] struct RawA { x: *const i8 } enum guestfs_A {} // opaque struct extern { fn to_RawA( src: *const guestfs_A ) -> RawA; } struct A { x: String } impl A { fn new(src: *const guestfs_A) -> A { let dst = unsafe {to_RawA(src)}; let c_str = unsafe { CStr::from_ptr(dst.x) }; let s = c_str.to_str().unwrap().to_string(); A{ x: s } } } ``` This is a little verbose and inefficient. # 2nd approach The above is easily done by 'bindgen', which automatically generates rust ffi bindings to C library. By using this, API struct is automatically generated. However, it requires a new dependency: libclang. # Question Which of these approaches is more preferable? Regards, Hiroyuki Katsura
Richard W.M. Jones
2019-Jun-05 07:32 UTC
Re: [Libguestfs] The way of implementing structs of Rust bindings
On Wed, Jun 05, 2019 at 03:33:51PM +0900, Hiroyuki Katsura wrote:> Hi, I'm now implementing generators of Rust, and I faced a problem. > > In order for the wrapper to 'understand' the struct passed from C API, it > is required to incorporate the definition of the struct in C into Rust > code. > > I have two approaches.I don't know a lot about Rust bindings, but ...> 1. Create raw struct(#[repr(C)]), which has the equivalent memory mapping > to C struct and access through this struct in Rust... this is what I did in nbdkit (although it is not generated, but written by hand in this case): https://github.com/libguestfs/nbdkit/blob/a5d94ab65110136680598677610868196ed71145/plugins/rust/src/lib.rs#L38> 2. Use bindgen to create ffi struct from guestfs.hWe have tended to avoid this in other languages because the results can be very poor quality (eg. swig bindings are usually terrible). However if the resulting code from bindgen is high quality and accurately covers all the features that libguestfs bindings need then we can consider it.> Each of them has advantages and disadvantages. > > # 1st approach > This is highly dependent on the implementation of API because it is subject > to the memory mapping of API struct. When the way struct is structured > changes in API, you have to modify Rust bindings. In order to avoid this > situation, you can define the same struct in C and Rust and then access the > API struct in C and then Rust accesses the data through this struct. This > means thatSure, I understand all this. We use the default C memory representation for the C compiler in use. Rust should be able to deal with this naturally.> # 2nd approach > > The above is easily done by 'bindgen', which automatically generates rust > ffi bindings to C library. By using this, API struct is automatically > generated. However, it requires a new dependency: libclang. > > # Question > > Which of these approaches is more preferable?Depends on the quality of the bindgen output IMO. If it's good, and covers all the features needed, use it. If not, generate the repr(C) structs. Rich. -- Richard Jones, Virtualization Group, Red Hat http://people.redhat.com/~rjones Read my programming and virtualization blog: http://rwmj.wordpress.com virt-builder quickly builds VMs from scratch http://libguestfs.org/virt-builder.1.html
Hiroyuki Katsura
2019-Jun-05 08:23 UTC
Re: [Libguestfs] The way of implementing structs of Rust bindings
Dear Richard,> We have tended to avoid this in other languages because the results > can be very poor quality (eg. swig bindings are usually terrible). > However if the resulting code from bindgen is high quality and > accurately covers all the features that libguestfs bindings need then > we can consider it.Thank you for your reply. Actually, I have not used bindgen and I cannot guarantee the quality. However, in this bindings, generated structs are accessed not from bindings user but from only Rust bindings. And required features are the only translation of structs, whose types are just char, char *, uint32_t, char [32], uint32_t, int32_t, int64_t and float. I tested guestfs.h and it looked good. But I am a newbie of developing libguestfs and I cannot assure the bindgen will not cause any problems, so I think I follow the convention for now. Regards, Hiroyuki 2019年6月5日(水) 16:32 Richard W.M. Jones <rjones@redhat.com>:> On Wed, Jun 05, 2019 at 03:33:51PM +0900, Hiroyuki Katsura wrote: > > Hi, I'm now implementing generators of Rust, and I faced a problem. > > > > In order for the wrapper to 'understand' the struct passed from C API, it > > is required to incorporate the definition of the struct in C into Rust > > code. > > > > I have two approaches. > > I don't know a lot about Rust bindings, but ... > > > 1. Create raw struct(#[repr(C)]), which has the equivalent memory mapping > > to C struct and access through this struct in Rust > > ... this is what I did in nbdkit (although it is not generated, but > written by hand in this case): > > > https://github.com/libguestfs/nbdkit/blob/a5d94ab65110136680598677610868196ed71145/plugins/rust/src/lib.rs#L38 > > > 2. Use bindgen to create ffi struct from guestfs.h > > We have tended to avoid this in other languages because the results > can be very poor quality (eg. swig bindings are usually terrible). > However if the resulting code from bindgen is high quality and > accurately covers all the features that libguestfs bindings need then > we can consider it. > > > Each of them has advantages and disadvantages. > > > > # 1st approach > > This is highly dependent on the implementation of API because it is > subject > > to the memory mapping of API struct. When the way struct is structured > > changes in API, you have to modify Rust bindings. In order to avoid this > > situation, you can define the same struct in C and Rust and then access > the > > API struct in C and then Rust accesses the data through this struct. This > > means that > > Sure, I understand all this. We use the default C memory > representation for the C compiler in use. Rust should be able to deal > with this naturally. > > > # 2nd approach > > > > The above is easily done by 'bindgen', which automatically generates rust > > ffi bindings to C library. By using this, API struct is automatically > > generated. However, it requires a new dependency: libclang. > > > > # Question > > > > Which of these approaches is more preferable? > > Depends on the quality of the bindgen output IMO. If it's good, and > covers all the features needed, use it. If not, generate the repr(C) > structs. > > Rich. > > -- > Richard Jones, Virtualization Group, Red Hat > http://people.redhat.com/~rjones > Read my programming and virtualization blog: http://rwmj.wordpress.com > virt-builder quickly builds VMs from scratch > http://libguestfs.org/virt-builder.1.html >