what if traits could have associated tests, to ensure that implementations of the trait behave correctly?

#rust

@w Huh, I've never thought of that. That's a very interesting idea!

My guess would be that there may be some technical difficulties for implementing this. But maybe that'd be worth exploring

@w Feels like something that could be achieved with compile-time reflection. Too bad it's gonna take years at least before we can get something like that

@diondokter @w I think we can do this today in user land by writing tests that take generic arguments constrained to the trait as input.

In languages like Haskell and PureScript, type classes (similar to traits in Rust) often have laws or properties that should hold, so it’s clear what the tests should cover.

For example: https://github.com/purescript-contrib/purescript-quickcheck-laws

I was so used to this idea that I went looking for the equivalent for Read/Write/Seek a while back: https://users.rust-lang.org/t/existing-tests-for-read-write-and-seek-traits/72991?u=paulyoung

GitHub - purescript-contrib/purescript-quickcheck-laws: QuickCheck powered law tests for PureScript's core typeclasses.

QuickCheck powered law tests for PureScript's core typeclasses. - purescript-contrib/purescript-quickcheck-laws

GitHub

@paulyoung @w how I imagine this would work is that you'd write an associated test. Then on a cargo test, it would find all types that implement the trait (even ones you the trait creator don't know about) and pass that through the test.

One thing though would be that you'd need a way to initialize every type in some way...

@diondokter @w with QuickCheck in Haskell/PureScript you typically define an instance of the Arbitrary type class (implement the trait in Rust)

https://pursuit.purescript.org/packages/purescript-quickcheck/8.0.1/docs/Test.QuickCheck.Arbitrary#t:Arbitrary

A basic version could perhaps require types to implement Default or similar.

Test.QuickCheck.Arbitrary - purescript-quickcheck - Pursuit

@diondokter @paulyoung @w Using a combination of generic tests and macros to generate them, you could make it very convenient to work with, but you’d still have to enumerate all the types you want to test.

This is really because logic for unknown types can have unanticipated effects, because arbitrary logic can fit in the type implementation (think, connecting to a database). Also if you’re searching for 1st party and 3rd party implementers, you can’t precisely (or even roughly) estimate the amount of computational work your tests will do.

@johnbchron @paulyoung @w it really depends on who runs the tests. Should it be run on crates.io? No

Run by the writer of the trait against everything that's out there? No

I think it should be run by users of the trait. When you import a trait, then it will run the tests against all it's impls in your project (and your dependencies maybe?)

@diondokter @johnbchron @w I think the person implementing the trait should opt in
@diondokter @paulyoung @w Great idea! Tests could just be provided trait methods with #[test]. If there are argument free constructors (even from dependencies, eg when a trait depends on Default), all those taking a ref would run from those. Types could provide own #[test] constructors in their absence, and it'd be a warning for an impl to have a test that can't be run in at least one monomorphization for lack of a suitable constructor.