@@ -152,14 +152,13 @@ impl<'a> IndividualTable<'a> {
152152 ///
153153 /// # Returns
154154 ///
155- /// The result type is `Option<T>`
156- /// where `T`: [`tskit::metadata::IndividualMetadata`](crate::metadata::IndividualMetadata).
157- /// `Some(T)` if there is metadata. `None` if the metadata field is empty for a given
158- /// row.
155+ /// * `Some(Ok(T))` if `row` is valid and decoding succeeded.
156+ /// * `Some(Err(_))` if `row` is not valid and decoding failed.
157+ /// * `None` if `row` is not valid.
159158 ///
160159 /// # Errors
161160 ///
162- /// * [`TskitError::IndexError `] if `row` is out of range .
161+ /// * [`TskitError::MetadataError `] if decoding fails .
163162 ///
164163 /// # Examples
165164 ///
@@ -190,15 +189,16 @@ impl<'a> IndividualTable<'a> {
190189 /// # let metadata = IndividualMetadata{x: 1};
191190 /// # assert!(tables.add_individual_with_metadata(0, None, None,
192191 /// # &metadata).is_ok());
193- /// // We know the metadata are here, so we unwrap the Result and the Option
192+ /// // We know the metadata are here, so we unwrap the Option and the Result:
194193 /// let decoded = tables.individuals().metadata::<IndividualMetadata>(0.into()).unwrap().unwrap();
195194 /// assert_eq!(decoded.x, 1);
196195 /// # }
197196 /// ```
198197 ///
199198 /// ## Checking for errors and absence of metadata
200199 ///
201- /// Handling both the possibility of error and optional metadata leads to some verbosity:
200+ /// The `Option<Result<_>>` return value allows all
201+ /// three return possibilities to be easily covered:
202202 ///
203203 /// ```
204204 /// # #[cfg(feature = "derive")] {
@@ -213,22 +213,112 @@ impl<'a> IndividualTable<'a> {
213213 /// # assert!(tables
214214 /// # .add_individual_with_metadata(0, None, None, &metadata)
215215 /// # .is_ok());
216- /// // First, check the Result.
217- /// let decoded_option = match tables
218- /// .individuals()
219- /// .metadata::<IndividualMetadata>(0.into())
216+ /// match tables.individuals().metadata::<IndividualMetadata>(0.into())
220217 /// {
221- /// Some(metadata_option) => metadata_option,
222- /// None => panic!("expected metadata"),
218+ /// Some(Ok(metadata)) => assert_eq!(metadata.x, 1),
219+ /// Some(Err(_)) => panic!("got an error??"),
220+ /// None => panic!("Got None??"),
223221 /// };
224- /// // Now, check the contents of the Option
225- /// match decoded_option {
226- /// Ok(metadata) => assert_eq!(metadata.x, 1),
227- /// Err(e) => panic!("error decoding metadata: {:?}", e),
222+ /// # }
223+ /// ```
224+ ///
225+ /// ## Attempting to use the wrong type.
226+ ///
227+ /// Let's define a mutation metadata type with the exact same fields
228+ /// as our individual metadata defined above:
229+ ///
230+ /// ```
231+ /// # #[cfg(feature = "derive")] {
232+ /// #[derive(serde::Serialize, serde::Deserialize, tskit::metadata::MutationMetadata)]
233+ /// #[serializer("serde_json")]
234+ /// struct MutationMetadata {
235+ /// x: i32,
236+ /// }
237+ /// # }
238+ /// ```
239+ ///
240+ /// This type has the wrong trait bound and will cause compilation to fail:
241+ ///
242+ #[ cfg_attr(
243+ feature = "derive" ,
244+ doc = r##"
245+ ```compile_fail
246+ # #[derive(serde::Serialize, serde::Deserialize, tskit::metadata::MutationMetadata)]
247+ # #[serializer("serde_json")]
248+ # struct MutationMetadata {
249+ # x: i32,
250+ # }
251+ # use tskit::TableAccess;
252+ # let mut tables = tskit::TableCollection::new(10.).unwrap();
253+ match tables.individuals().metadata::<MutationMetadata>(0.into())
254+ {
255+ Some(Ok(metadata)) => assert_eq!(metadata.x, 1),
256+ Some(Err(_)) => panic!("got an error??"),
257+ None => panic!("Got None??"),
258+ };
259+ ```
260+ "##
261+ ) ]
262+ ///
263+ /// ## Limitations: different type, same trait bound
264+ ///
265+ /// Finally, let us consider a different struct that has identical
266+ /// fields to `IndividualMetadata` defined above and also implements
267+ /// the correct trait:
268+ ///
269+ /// ```
270+ /// # #[cfg(feature = "derive")] {
271+ /// #[derive(serde::Serialize, serde::Deserialize, tskit::metadata::IndividualMetadata)]
272+ /// #[serializer("serde_json")]
273+ /// struct IndividualMetadataToo {
274+ /// x: i32,
228275 /// }
229276 /// # }
230277 /// ```
231- pub fn metadata < T : metadata:: MetadataRoundtrip > (
278+ ///
279+ /// Let's walk through a detailed example:
280+ ///
281+ /// ```
282+ /// # #[cfg(feature = "derive")] {
283+ /// # use tskit::TableAccess;
284+ /// # #[derive(serde::Serialize, serde::Deserialize, tskit::metadata::IndividualMetadata)]
285+ /// # #[serializer("serde_json")]
286+ /// # struct IndividualMetadata {
287+ /// # x: i32,
288+ /// # }
289+ /// # #[derive(serde::Serialize, serde::Deserialize, tskit::metadata::IndividualMetadata)]
290+ /// # #[serializer("serde_json")]
291+ /// # struct IndividualMetadataToo {
292+ /// # x: i32,
293+ /// # }
294+ /// // create a mutable table collection
295+ /// let mut tables = tskit::TableCollection::new(100.).unwrap();
296+ /// // Create some metadata based on our FIRST type
297+ /// let metadata = IndividualMetadata { x: 1 };
298+ /// // Add a row with our metadata
299+ /// assert!(tables.add_individual_with_metadata(0, None, None, &metadata).is_ok());
300+ /// // Trying to fetch using our SECOND type as the generic type works!
301+ /// match tables.individuals().metadata::<IndividualMetadataToo>(0.into())
302+ /// {
303+ /// Some(Ok(metadata)) => assert_eq!(metadata.x, 1),
304+ /// Some(Err(_)) => panic!("got an error??"),
305+ /// None => panic!("Got None??"),
306+ /// };
307+ /// # }
308+ /// ```
309+ ///
310+ /// What is going on here?
311+ /// Both types satisfy the same trait bound ([`metadata::IndividualMetadata`])
312+ /// and their data fields look identical to `serde_json`.
313+ /// Thus, one is exchangeable for the other because they have the exact same
314+ /// *behavior*.
315+ ///
316+ /// However, it is also true that this is (often/usually/always) not exactly what we want.
317+ /// We are experimenting with encapsulation APIs involving traits with
318+ /// [associated
319+ /// types](https://doc.rust-lang.org/book/ch19-03-advanced-traits.html#specifying-placeholder-types-in-trait-definitions-with-associated-types) to enforce at *compile time* that exactly one type (`struct/enum`, etc.) is a valid
320+ /// metadata type for a table.
321+ pub fn metadata < T : metadata:: IndividualMetadata > (
232322 & ' a self ,
233323 row : IndividualId ,
234324 ) -> Option < Result < T , TskitError > > {
0 commit comments