Upgrade to Pro — share decks privately, control downloads, hide ads and more …

How to Call Rust Code From C... Or Any Language

How to Call Rust Code From C... Or Any Language

Yehuda Katz

August 02, 2015
Tweet

More Decks by Yehuda Katz

Other Decks in Technology

Transcript

  1. C Code char*  trace_name(Trace*  trace)  {      /*  impl

     */   }   //  Questions:   //     //  *  Can  trace_name  retain  an  alias  to  the  Trace*?   //  *  Can  trace_name  mutate  the  Trace*?   //  *  Can  a  caller  mutate  the  Trace*  during  trace_name?   //  *  Who  is  responsible  to  freeing  the  char*?
  2. Rust Code fn  trace_name(trace:  &Trace)  -­‐>  String  {    

     /*  impl  */   }   //  Questions:   //     //  *  Can  trace_name  retain  an  alias  to  the  Trace?  No.   //  *  Can  trace_name  mutate  the  Trace?  No.   //  *  Can  a  caller  mutate  the  Trace  during  trace_name?  No.   //  *  Who  is  responsible  to  freeing  the  String?  The  caller.
  3. Extern Rust Functions #[no_mangle]   extern  "C"  fn  trace_name(trace:  &Trace)

     -­‐>  String  {
    /*  impl  */
 }
 
 //  Questions:   //     //  *  Can  trace_name  retain  an  alias  to  the  Trace?  No.   //  *  Can  trace_name  mutate  the  Trace?  No.   //  *  Can  a  caller  mutate  the  Trace  during  trace_name?  No.   //  *  Who  is  responsible  to  freeing  the  String?  The  caller.   //  *  TLDR:  The  same  story!
  4. Rust is a DSL for describing ownership concepts that you

    have to think about when writing C or C++ Me, Now
  5. Calling Rust From C extern  "C"  fn  trace_id(trace:  &Trace)  -­‐>

     u64; uint64_t  get_trace_id(Trace*  trace)  {      return  trace_id(trace);   }
  6. The Process 1. Identify and implement the shared type definitions

    in Rust and C 2. Use Rust types to flesh out the ownership and mutability rules 3. Decide how to express and "enforce" the requirements from C
  7. Calling Rust From C extern  "C"  fn  trace_id(trace:  &Trace)  -­‐>

     u64;   What is this type in terms of C? Go-to answer for simple types:
 typedef  u64  uint64_t;
  8. Calling Rust From C extern  "C"  fn  trace_id(trace:  &Trace)  -­‐>

     u64;   uint64_t  trace_id(Trace*  trace); What is this type in terms of C? Go-to answer for simple types:
 typedef  u64  uint64_t;
  9. Calling Rust From C extern  "C"  fn  new_trace()  -­‐>  Box<Trace>;

      What is this type in terms of C? Go-to answer for structures:
 typedef  Trace  void  *;
  10. Calling Rust From C extern  "C"  fn  new_trace()  -­‐>  Box<Trace>;

      typedef  void*  Trace;   Trace  new_trace(); What is this type in terms of C? Go-to answer for structures:
 typedef  Trace  void  *;
  11. Simple Example struct  Point  {      x:  i64,  

       y:  i64   }   #[no_mangle]   extern  "C"  fn  new_point(x:  i64,  y:  i64)  -­‐>  Box<Point>  {      Box::new(Point  {  x:  x,  y:  y  })   }   #[no_mangle]   extern  "C"  fn  line_len(p1:  &Point,  p2:  &Point)  -­‐>  f64  {      /*  impl  */   }
  12. Simple Scaffolding From C typedef  void*  Point;   typedef  int64_t

     i64;   typedef  double  f64;   Point  new_point(i64  x,  i64  y);   f64  line_len(Point  p1,  Point  p2);   Point  make_point(i64  x,  i64  y)  {      return  new_point(x,  y);   }   f64  calculate_len(Point  p1,  Point  p2)  {      return  line_len(p1,  p2);   }
  13. Simple Mapping / My Opinion Rust C Portable Copy Types

    u32,  i64,  usize uint32_t,  int64_t,  uintptr_t Your Structures Box<T> typedef  to  void  * Borrowed Structures &T typedef  to  void  * Strings and Vectors String  /  Vec<T>  /  &str  /  &[T] Custom Transport
  14. What we did so far #[no_mangle]   extern  "C"  fn

     line_len(p1:  &Point,  p2:  &Point)  -­‐>  f64  {      /*  impl  */   } typedef  void*  Point;   typedef  double  f64;   f64  line_len(Point  p1,  Point  p2);
  15. What we did so far #[no_mangle]   extern  "C"  fn

     line_len(p1:  &Point,  p2:  &Point)  -­‐>  f64  {      /*  impl  */   } typedef  void*  Point;   typedef  double  f64;   f64  line_len(Point  p1,  Point  p2);
  16. What we did so far #[no_mangle]   extern  "C"  fn

     line_len(p1:  &Point,  p2:  &Point)  -­‐>  f64  {      /*  impl  */   } typedef  void*  Point;   typedef  double  f64;   f64  line_len(Point  p1,  Point  p2);
  17. And now, docs! typedef  void*  Point;   typedef  double  f64;

      /**      line_len  takes  two  Point  structs.  You  can  rely  on  the  fact  that  it        will  not  create  aliases  to  the  Points  that  outlive  the  invocation.      Point  should  be  memory  that  is  valid  and  immutable  for  the  duration      of  the  call  to  the  line_len  function.   */   f64  line_len(const  Point  p1,  const  Point  p2);
  18. Mapping typedef  void*  Poing;   typedef  double  f64;   /**

         line_len  takes  two  Point  structs.  You  can  rely  on  the  fact  that  it        will  not  create  aliases  to  the  Points  that  outlive  the  invocation.      Point  should  be  memory  that  is  valid  and  immutable  for  the  duration      of  the  call  to  the  line_len  function.   */   f64  line_len(const  Point  p1,  const  Point  p2); #[no_mangle]   extern  "C"  fn  line_len(p1:  &Point,  p2:  &Point)  -­‐>  f64  {      /*  impl  */   }
  19. Mapping typedef  void*  Poing;   typedef  double  f64;   /**

         line_len  takes  two  Point  structs.  You  can  rely  on  the  fact  that  it        will  not  create  aliases  to  the  Points  that  outlive  the  invocation.      Point  should  be  memory  that  is  valid  and  immutable  for  the  duration      of  the  call  to  the  line_len  function.   */   f64  line_len(const  Point  p1,  const  Point  p2); #[no_mangle]   extern  "C"  fn  line_len(p1:  &Point,  p2:  &Point)  -­‐>  f64  {      /*  impl  */   }
  20. Mapping typedef  void*  Poing;   typedef  double  f64;   /**

         line_len  takes  two  Point  structs.  You  can  rely  on  the  fact  that  it        will  not  create  aliases  to  the  Points  that  outlive  the  invocation.      Point  should  be  memory  that  is  valid  and  immutable  for  the  duration      of  the  call  to  the  line_len  function.   */   f64  line_len(const  Point  p1,  const  Point  p2); #[no_mangle]   extern  "C"  fn  line_len(p1:  &Point,  p2:  &Point)  -­‐>  f64  {      /*  impl  */   }
  21. Mapping typedef  void*  Poing;   typedef  double  f64;   /**

         line_len  takes  two  Point  structs.  You  can  rely  on  the  fact  that  it        will  not  create  aliases  to  the  Points  that  outlive  the  invocation.      Point  should  be  memory  that  is  valid  and  immutable  for  the  duration      of  the  call  to  the  line_len  function.   */   f64  line_len(const  Point  p1,  const  Point  p2); #[no_mangle]   extern  "C"  fn  line_len(p1:  &Point,  p2:  &Point)  -­‐>  f64  {      /*  impl  */   }
  22. Rust is a DSL for describing ownership concepts that you

    have to think about when writing C or C++
  23. #include  <ownership.h>;   typedef  void*  Point;   typedef  double  f64;

      int  main()  {      Cell  p1  =  NEW_CELL(new_point(10,  20));      Cell  p2  =  NEW_CELL(new_point(30,  10));      printf("%f",  length(p1,  p2));   }   double  length(Cell  c1,  Cell  c2)  {      Point  p1  =  BORROW(c1);      Point  p2  =  BORROW(c2);      return  line_len(p1,  p2);   }
  24. #include  <ownership.h>;   typedef  void*  Point;   typedef  double  f64;

      int  main()  {      Cell  c  =  NEW_CELL(new_point(10,  20));      Point  p1  =  TRANSFER(c);      print_and_dispose(p1);      Point  p2  =  BORROW(c);   }
  25. #include  <ownership.h>;   typedef  void*  Point;   typedef  double  f64;

      int  main()  {      Cell  c  =  NEW_CELL(new_point(10,  20));      Point  p1  =  TRANSFER(c);      print_and_dispose(p1);      Point  p2  =  BORROW(c);   } DYNAMIC  ERROR
  26. Not Covered / My Opinions • Non-Simple return types (out-pointer

    + error boolean) • Non-opaque Rust structures in C (mostly YAGNI) • Dynamic ownership implementation (embedding-specific) • Catching panics (catch_panic coming soon!) • Paranoid extern functions (a different programming pattern)