Generics

  1. // wip
  2. struct Repo<T> {
  3. db DB
  4. }
  5. struct User {
  6. id int
  7. name string
  8. }
  9. struct Post {
  10. id int
  11. user_id int
  12. title string
  13. body string
  14. }
  15. fn new_repo<T>(db DB) Repo<T> {
  16. return Repo<T>{db: db}
  17. }
  18. // This is a generic function. V will generate it for every type it's used with.
  19. fn (r Repo<T>) find_by_id(id int) ?T {
  20. table_name := T.name // in this example getting the name of the type gives us the table name
  21. return r.db.query_one<T>('select * from $table_name where id = ?', id)
  22. }
  23. db := new_db()
  24. users_repo := new_repo<User>(db) // returns Repo<User>
  25. posts_repo := new_repo<Post>(db) // returns Repo<Post>
  26. user := users_repo.find_by_id(1)? // find_by_id<User>
  27. post := posts_repo.find_by_id(1)? // find_by_id<Post>

Currently generic function definitions must declare their type parameters, but in future V will infer generic type parameters from single-letter type names in runtime parameter types. This is why find_by_id can omit <T>, because the receiver argument r uses a generic type T.

Another example:

  1. fn compare<T>(a T, b T) int {
  2. if a < b {
  3. return -1
  4. }
  5. if a > b {
  6. return 1
  7. }
  8. return 0
  9. }
  10. // compare<int>
  11. println(compare(1, 0)) // Outputs: 1
  12. println(compare(1, 1)) // 0
  13. println(compare(1, 2)) // -1
  14. // compare<string>
  15. println(compare('1', '0')) // Outputs: 1
  16. println(compare('1', '1')) // 0
  17. println(compare('1', '2')) // -1
  18. // compare<f64>
  19. println(compare(1.1, 1.0)) // Outputs: 1
  20. println(compare(1.1, 1.1)) // 0
  21. println(compare(1.1, 1.2)) // -1