THRIFT-4099: Derive Hash trait for Rust structs
Client: rs

This closes #1246
diff --git a/compiler/cpp/src/thrift/generate/t_rs_generator.cc b/compiler/cpp/src/thrift/generate/t_rs_generator.cc
index 78794a5..c34ed17 100644
--- a/compiler/cpp/src/thrift/generate/t_rs_generator.cc
+++ b/compiler/cpp/src/thrift/generate/t_rs_generator.cc
@@ -245,6 +245,13 @@
   // rendered code is calling such a function it has to dereference `v`.
   bool needs_deref_on_container_write(t_type* ttype);
 
+  // Return the variable (including all dereferences) required to write values from a rust container
+  // to the output protocol. For example, if you were iterating through a container and using the temp
+  // variable `v` to represent each element, then `ttype` is the type stored in the container and
+  // `base_var` is "v". The return value is the actual string you will have to use to properly reference
+  // the temp variable for writing to the output protocol.
+  string string_container_write_variable(t_type* ttype, const string& base_var);
+
   // Write the code to read bytes from the wire into the given `t_struct`. `struct_name` is the
   // actual Rust name of the `t_struct`. If `struct_type` is `T_ARGS` then all struct fields are
   // necessary. Otherwise, the field's default optionality is used.
@@ -377,6 +384,9 @@
   // Write the documentation for a struct, service-call or other documentation-annotated element.
   void render_rustdoc(t_doc* tdoc);
 
+  // Return `true` if the true type of `ttype` is a thrift double, `false` otherwise.
+  bool is_double(t_type* ttype);
+
   // Return a string representing the rust type given a `t_type`.
   string to_rust_type(t_type* ttype, bool ordered_float = true);
 
@@ -655,10 +665,7 @@
       f_gen_ << tvalue->get_integer();
       break;
     case t_base_type::TYPE_DOUBLE:
-      f_gen_
-        << indent()
-        << "OrderedFloat::from(" << tvalue->get_double() << ")"
-        << endl;
+      f_gen_ << "OrderedFloat::from(" << tvalue->get_double() << " as f64)";
       break;
     default:
       throw "cannot generate const value for " + t_base_type::t_base_name(tbase_type->get_base());
@@ -826,7 +833,7 @@
 
 void t_rs_generator::render_enum_definition(t_enum* tenum, const string& enum_name) {
   render_rustdoc((t_doc*) tenum);
-  f_gen_ << "#[derive(Copy, Clone, Debug, Eq, Ord, PartialEq, PartialOrd)]" << endl;
+  f_gen_ << "#[derive(Copy, Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]" << endl;
   f_gen_ << "pub enum " << enum_name << " {" << endl;
   indent_up();
 
@@ -965,7 +972,7 @@
   t_rs_generator::e_struct_type struct_type
 ) {
   render_rustdoc((t_doc*) tstruct);
-  f_gen_ << "#[derive(Clone, Debug, Eq, Ord, PartialEq, PartialOrd)]" << endl;
+  f_gen_ << "#[derive(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]" << endl;
   f_gen_ << visibility_qualifier(struct_type) << "struct " << struct_name << " {" << endl;
 
   // render the members
@@ -1311,7 +1318,7 @@
     throw "cannot generate rust enum with 0 members"; // may be valid thrift, but it's invalid rust
   }
 
-  f_gen_ << "#[derive(Clone, Debug, Eq, Ord, PartialEq, PartialOrd)]" << endl;
+  f_gen_ << "#[derive(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]" << endl;
   f_gen_ << "pub enum " << union_name << " {" << endl;
   indent_up();
 
@@ -1543,7 +1550,7 @@
   string ref(list_var_is_ref ? "" : "&");
   f_gen_ << indent() << "for e in " << ref << list_var << " {" << endl;
   indent_up();
-  render_type_sync_write(needs_deref_on_container_write(elem_type) ? "*e" : "e", true, elem_type);
+  render_type_sync_write(string_container_write_variable(elem_type, "e"), true, elem_type);
   f_gen_ << indent() << "o_prot.write_list_end()?;" << endl;
   indent_down();
   f_gen_ << indent() << "}" << endl;
@@ -1564,7 +1571,7 @@
   string ref(set_var_is_ref ? "" : "&");
   f_gen_ << indent() << "for e in " << ref << set_var << " {" << endl;
   indent_up();
-  render_type_sync_write(needs_deref_on_container_write(elem_type) ? "*e" : "e", true, elem_type);
+  render_type_sync_write(string_container_write_variable(elem_type, "e"), true, elem_type);
   f_gen_ << indent() << "o_prot.write_set_end()?;" << endl;
   indent_down();
   f_gen_ << indent() << "}" << endl;
@@ -1587,13 +1594,30 @@
   string ref(map_var_is_ref ? "" : "&");
   f_gen_ << indent() << "for (k, v) in " << ref << map_var << " {" << endl;
   indent_up();
-  render_type_sync_write(needs_deref_on_container_write(key_type) ? "*k" : "k", true, key_type);
-  render_type_sync_write(needs_deref_on_container_write(val_type) ? "*v" : "v", true, val_type);
+  render_type_sync_write(string_container_write_variable(key_type, "k"), true, key_type);
+  render_type_sync_write(string_container_write_variable(val_type, "v"), true, val_type);
   f_gen_ << indent() << "o_prot.write_map_end()?;" << endl;
   indent_down();
   f_gen_ << indent() << "}" << endl;
 }
 
+string t_rs_generator::string_container_write_variable(t_type* ttype, const string& base_var) {
+  bool type_needs_deref = needs_deref_on_container_write(ttype);
+  bool type_is_double = is_double(ttype);
+
+  string write_variable;
+
+  if (type_is_double && type_needs_deref) {
+    write_variable = "(*" + base_var + ")";
+  } else if (type_needs_deref) {
+    write_variable = "*" + base_var;
+  } else {
+    write_variable = base_var;
+  }
+
+  return write_variable;
+}
+
 bool t_rs_generator::needs_deref_on_container_write(t_type* ttype) {
   ttype = get_true_type(ttype);
   return ttype->is_base_type() && !ttype->is_string();
@@ -2864,6 +2888,18 @@
   f_gen_ << indent() << ")" << endl;
 }
 
+bool t_rs_generator::is_double(t_type* ttype) {
+  ttype = get_true_type(ttype);
+  if (ttype->is_base_type()) {
+    t_base_type::t_base tbase = ((t_base_type*)ttype)->get_base();
+    if (tbase == t_base_type::TYPE_DOUBLE) {
+      return true;
+    }
+  }
+
+  return false;
+}
+
 string t_rs_generator::to_rust_type(t_type* ttype, bool ordered_float) {
   // ttype = get_true_type(ttype); <-- recurses through as many typedef layers as necessary
   if (ttype->is_base_type()) {
diff --git a/lib/rs/src/transport/mem.rs b/lib/rs/src/transport/mem.rs
index 8ec2a98..97ec503 100644
--- a/lib/rs/src/transport/mem.rs
+++ b/lib/rs/src/transport/mem.rs
@@ -109,7 +109,7 @@
         let buf = {
             let b = self.write_buffer_as_ref();
             let mut b_ret = vec![0; b.len()];
-            b_ret.copy_from_slice(&b);
+            b_ret.copy_from_slice(b);
             b_ret
         };
 
diff --git a/lib/rs/test/thrifts/Base_One.thrift b/lib/rs/test/thrifts/Base_One.thrift
index ceb1207..3da083d 100644
--- a/lib/rs/test/thrifts/Base_One.thrift
+++ b/lib/rs/test/thrifts/Base_One.thrift
@@ -31,6 +31,10 @@
 
 const list<Temperature> Temperatures = [10, 11, 22, 33]
 
+// IMPORTANT: temps should end with ".0" because this tests
+// that we don't have a problem with const float list generation
+const list<double> CommonTemperatures = [300.0, 450.0]
+
 const double MealsPerDay = 2.5;
 
 struct Noodle {
@@ -48,6 +52,21 @@
   1: Size size
 }
 
+struct MeasuringCup {
+  1: double millis
+}
+
+union MeasuringAids {
+  1: MeasuringSpoon spoon
+  2: MeasuringCup cup
+}
+
+struct CookingTemperatures {
+  1: set<double> commonTemperatures
+  2: list<double> usedTemperatures
+  3: map<double, double> fahrenheitToCentigradeConversions
+}
+
 struct Recipe {
   1: string recipeName
   2: string cuisine