diff --git a/src/rt/rust_shape.cpp b/src/rt/rust_shape.cpp
index 8e99c41ea02..6a586860cd6 100644
--- a/src/rt/rust_shape.cpp
+++ b/src/rt/rust_shape.cpp
@@ -42,10 +42,21 @@ type_param::make(const type_desc **tydescs, unsigned n_tydescs,
     return ptrs;
 }
 
+// Constructs type parameters from an object shape. This is a bit messy,
+// because it requires that the object shape have a specific format.
 type_param *
-type_param::from_obj_shape(const uint8_t *sp, arena &arena) {
-    // TODO
-    abort();
+type_param::from_obj_shape(const uint8_t *sp, ptr dp, arena &arena) {
+    uint8_t shape = *sp++; assert(shape == SHAPE_STRUCT);
+    get_u16_bump(sp);   // Skip over the size.
+    shape = *sp++; assert(shape == SHAPE_PTR);
+    shape = *sp++; assert(shape == SHAPE_STRUCT);
+
+    unsigned n_tydescs = get_u16_bump(sp);
+
+    // Type descriptors start right after the reference count.
+    const type_desc **descs = (const type_desc **)(dp + sizeof(uintptr_t));
+
+    return make(descs, n_tydescs, arena);
 }
 
 
diff --git a/src/rt/rust_shape.h b/src/rt/rust_shape.h
index e735bfa3d1b..0302338e26d 100644
--- a/src/rt/rust_shape.h
+++ b/src/rt/rust_shape.h
@@ -46,11 +46,18 @@ const uint8_t SHAPE_OBJ = 19u;
 const uint8_t SHAPE_RES = 20u;
 const uint8_t SHAPE_VAR = 21u;
 
+#ifdef _LP64
+const uint8_t SHAPE_PTR = SHAPE_U64;
+#else
+const uint8_t SHAPE_PTR = SHAPE_U32;
+#endif
+
 
 // Forward declarations
 
 struct rust_obj;
 struct size_align;
+class ptr;
 class type_param;
 
 
@@ -129,6 +136,21 @@ struct tag_info {
 };
 
 
+// Utility functions
+
+inline uint16_t
+get_u16(const uint8_t *addr) {
+    return *reinterpret_cast<const uint16_t *>(addr);
+}
+
+inline uint16_t
+get_u16_bump(const uint8_t *&addr) {
+    uint16_t result = get_u16(addr);
+    addr += sizeof(uint16_t);
+    return result;
+}
+
+
 // Contexts
 
 // The base context, an abstract class. We use the curiously recurring
@@ -166,8 +188,6 @@ public:
 protected:
     inline uint8_t peek() { return *sp; }
 
-    static inline uint16_t get_u16(const uint8_t *addr);
-    static inline uint16_t get_u16_bump(const uint8_t *&addr);
     inline size_align get_size_align(const uint8_t *&addr);
 
 private:
@@ -226,7 +246,8 @@ public:
     const type_param *params;   // subparameters
 
     // Creates type parameters from an object shape description.
-    static type_param *from_obj_shape(const uint8_t *sp, arena &arena);
+    static type_param *from_obj_shape(const uint8_t *sp, ptr dp,
+                                      arena &arena);
 
     template<typename T>
     inline void set(ctxt<T> *cx) {
@@ -284,20 +305,6 @@ ctxt<T>::walk_reset(bool align) {
     sp = old_sp;
 }
 
-template<typename T>
-uint16_t
-ctxt<T>::get_u16(const uint8_t *addr) {
-    return *reinterpret_cast<const uint16_t *>(addr);
-}
-
-template<typename T>
-uint16_t
-ctxt<T>::get_u16_bump(const uint8_t *&addr) {
-    uint16_t result = get_u16(addr);
-    addr += sizeof(uint16_t);
-    return result;
-}
-
 template<typename T>
 size_align
 ctxt<T>::get_size_align(const uint8_t *&addr) {
@@ -892,17 +899,13 @@ data<T,U>::walk_obj_contents(bool align, ptr &dp) {
     uint8_t *box_ptr = bump_dp<uint8_t *>(dp);
     type_desc *subtydesc =
         *reinterpret_cast<type_desc **>(box_ptr + sizeof(void *));
-    ptr obj_closure_dp(*box_ptr + sizeof(void *));
+    ptr obj_closure_dp(box_ptr + sizeof(void *));
 
-    // FIXME: Should be type_param::from_obj_shape() below.
     arena arena;
-    type_param *params = type_param::from_tydesc(subtydesc, arena);
+    type_param *params = type_param::from_obj_shape(subtydesc->shape,
+                                                    obj_closure_dp, arena);
     T sub(*static_cast<T *>(this), subtydesc->shape, params,
           subtydesc->shape_tables, obj_closure_dp);
-
-    print print(sub);
-    print.walk(false);
-
     sub.walk(true);
 }