diff --git a/rules.org b/rules.org
index d4db7a0ba62879a3ad5847ae7f6792d7263bf8f9..ddeabca77364efcb6cb71cc64f78be09bcf9188b 100644
--- a/rules.org
+++ b/rules.org
@@ -155,7 +155,7 @@ package you're working on.  But please try to always follow these rules:
 
    An exception for this is xAOD data classes, where the member names are
    exposed via ROOT for analysis.
- 
+
 
  - *Do not start any other names with* =m_=. [<<m-prefix-reserved>>]
 
@@ -208,14 +208,14 @@ class Track;
 enum State { green, yellow, red };
 #+END_EXAMPLE
 
- - *Typedef names should start with an uppercase letter if they are public
+ - *Type alias (typedef) names should start with an uppercase letter if they are public
     and treated as classes.*  [<<typedef-naming>>]
 
    #+BEGIN_EXAMPLE
-typedef vector<MCParticleKinematics*> TrackVector;
+using TrackVector = vector<MCParticleKinematics*>;
 #+END_EXAMPLE
 
- - *Alternatively, a typedef name may start with a lower-case letter
+ - *Alternatively, a type alias (typedef) name may start with a lower-case letter
    and end with* =_t=.  [<<typedef-naming-2>>]
 
    This form should be reserved for type names which are not treated
@@ -223,7 +223,7 @@ typedef vector<MCParticleKinematics*> TrackVector;
    are private to a class.
 
   #+BEGIN_EXAMPLE
-typedef unsigned int mycounter_t;
+using mycounter_t = unsigned int;
 #+END_EXAMPLE
 
 
@@ -347,7 +347,7 @@ public:
    details which are inside its header.  This saves time in compilation
    and avoids an apparent dependency upon the =Line= header file.
 
-   Be careful, however: this does not work if =Line= is actually a =typedef=
+   Be careful, however: this does not work if =Line= is actually an alias
    (as is the case, for example, with many of the xAOD classes).
 
  - *Each header file must contain the declaration for one class only, except for 
@@ -457,8 +457,8 @@ vector<int> x;  // Missing std!
    Otherwise, the =using= may serve to hide problems with missing
    namespace qualifications in the headers.
 
-   This rule does not apply when =using= is used to define a new name
-   for a type (similarly to =typedef=).
+   This rule does not apply when =using= is used to define a type alias
+   (similarly to =typedef=).
 
 
 ** Control flow
@@ -497,6 +497,16 @@ for (std::vector<int>::const_iterator it = v.begin();
    multiple iterators within the loop.  But most simple loops over
    STL ranges are more simply written with a range-based for.
 
+   As of C++20, you can initialize additional variables in a
+   range-based for:
+   #+BEGIN_EXAMPLE
+void foo (const std::vector<float>& v) {
+  for (int i = 0; float f : v) {
+    std::cout << i++ << " " << f << "\n";
+  }
+}
+#+END_EXAMPLE
+
 
  - *Switch statements should have a default clause.*  [<<switch-default>>]
 
@@ -655,6 +665,20 @@ void A::foo()
     defining a symbol for it.
 
 
+ - *Use <numbers> header for mathematical constants.*  [<<math-constants>>]
+
+   Basic mathematical constants are available in the header
+   =<numbers>=.  Use these in preference to the =M_= constants from
+   =math.h= or explicit definitions:
+    #+BEGIN_EXAMPLE
+#include <numbers>
+#include <cmath>
+double f (double x) {
+  return std::sin (x * std::numbers::pi);
+}
+#+END_EXAMPLE
+
+
  - *Declare each type of variable in a separate declaration statement, and
    do not declare different types (e.g. int and int pointer) in one declaration statement.*  [<<separate-declarations>>]
 
@@ -1211,7 +1235,18 @@ int convertAndBuffer (int* buf, float x)
 #+END_EXAMPLE
 
    (As a special case, you can safely convert any pointer type to or from
-   a =char*=.)  The proper way to do such a conversion is with a union.
+   a =char*=.)  The proper way to do such a conversion is with
+   a =std::bit_cast=:
+   #+BEGIN_EXAMPLE
+#include <bit>
+int convertAndBuffer (int* buf, float x)
+{
+  *buf = std::bit_cast<int> (x);
+  return *buf;
+}
+#+END_EXAMPLE
+    Prior to C++20, the recommended way to do this was with a union,
+    but that should not be used for new code.
 
 
 ** The class interface
@@ -1423,6 +1458,38 @@ a = b = c;
     will assign =c= to =b= and then =b= to =a= as is the case with built-in objects.
 
 
+ - *Use* =std::span= *to represent and pass a bounded region of memory.* [<<span>>]
+
+   In particular, use =std::span= instead of passing a pointer with
+   a separate element count (or even worse, a pointer to an array with
+   no bounds information).
+
+   So you can use this:
+   #+BEGIN_EXAMPLE
+#include <span>
+int sum (const std::span<int>& s)
+{
+  int ret = 0;
+  for (int i : s) ret += i;
+  return ret;
+}
+#+END_EXAMPLE
+   instead of
+   #+BEGIN_EXAMPLE
+int sum (const int* p, size_t n)
+{
+  int ret = 0;
+  for (size_t i = 0; i < n; i++) ret += p[i];
+  return ret;
+}
+#+END_EXAMPLE
+
+   One might expect that =std::span= would include an =at()= method,
+   to allow indexing with bounds checking, but that is only available
+   in C++23.  In the meantime, =CxxUtils::span= is very similar to
+   =std::span= but does implement =at()=.
+
+
 *** =const= correctness
 
 
@@ -1493,6 +1560,56 @@ ostream& operator<<(ostream& out, const String& s);
     closely related member functions is very confusing and is not
     recommended.
 
+
+*** Comparisons
+
+ - *Define comparions for custom types using* =operator=== *and* =operator<=>=.  [<<comparisons>>]
+
+   Comparions of for a custom class should be written using
+   =operator=== (for equality/inequality) and =operator<=>= (for ordering).
+   The compiler will supply the other comparison operators
+   (=operator!==, =operator<=, etc.) automatically.  Where possible,
+   =operator<=>= is best defined using the same operator on the
+   members involved.  Examples:
+    #+BEGIN_EXAMPLE
+#include <compare>
+#include <tuple>
+
+class S
+{
+public:
+  bool operator== (const S& other)
+  {
+    return m_key == other.m_key;
+  }
+  std::strong_ordering operator<=> (const S& other)
+  {
+    return m_key <=> other.m_key;
+  }
+private:
+  int m_key;
+};
+
+
+class Version
+{
+public:
+  bool operator== (const Version& other)
+  {
+    return m_major == other.m_major && m_minor == other.m_minor;
+  }
+  std::strong_ordering operator<=> (const Version& other)
+  {
+    return std::make_tuple (m_major, m_minor) <=>
+           std::make_tuple (other.m_major, other.m_minor);
+  }
+private:
+  int m_major;
+  int m_minor;
+};
+#+END_EXAMPLE
+
+
 ** =new= and =delete=
 
 
@@ -2340,7 +2457,7 @@ safer possibilities.
 
     =scanf= and =printf= are not type-safe and they are not
     extensible. Use =operator>>= and =operator<<= associated with C++ streams
-    instead, along with =std::format= to handle formatting (see [[use-format]]).
+    instead, along with =std::format= to handle formatting (see use-format [[use-format]]).
     iostream and stdio functions should never be mixed.
 
     Example:
@@ -2548,7 +2665,7 @@ some machines, left-to-right on others. -- end note ]
     That way, they won't clutter up the header file.
 
 
- - *Do not declare your own typedef for booleans. Use the bool type of C++ for booleans.*  [<<use-bool>>]
+ - *Do not declare your own alias for booleans. Use the bool type of C++ for booleans.*  [<<use-bool>>]
 
     The =bool= type was not implemented in C. Programmers usually got around
     the problem by typedefs and/or const declarations. This is no longer
@@ -2638,37 +2755,37 @@ some machines, left-to-right on others. -- end note ]
    level.
 
 
- - *Avoid using typedef to create aliases for classes.* [<<avoid-typedef>>]
+ - *Avoid creating type aliases for classes.* [<<avoid-typedef>>]
 
-   Typedefs are a serious impediment in large systems. While they
-   simplify code for the original author, a system filled with typedefs
+   Type aliases (typedefs) are a serious impediment in large systems. While they
+   simplify code for the original author, a system filled with aliases
    can be difficult to understand. If the reader encounters a class =A=, he
    or she can find an =#include= with ``A.h'' in it to locate a description of =A=;
-   but typedefs carry no context that tell a reader where to find a
+   but aliases carry no context that tell a reader where to find a
    definition. Moreover, most of the generic characteristics obtained
-   with typedefs are better handled by object oriented techniques, like
+   with aliases are better handled by object oriented techniques, like
    polymorphism.
 
-   Typedefs are acceptible where they provide part of the expected
+   Aliases are acceptible where they provide part of the expected
    interface for a class, for example =value_type=, etc. 
    in classes used with STL algorithms.  They are often indispensible
    in template programming and metaprogramming, and are also part
    of how xAOD classes and POOL converters are typically defined.
 
    In other contexts, they should be used with care, and should generally
-   be accompanied with a comment giving the rationale for the typedef.
+   be accompanied with a comment giving the rationale for the alias.
 
-   Typedefs may be used as a ``customization point;''  that is, to allow
+   Aliases may be used as a ``customization point;''  that is, to allow
    the possibility of changing a type in the future.
    For example, the auxiliary store code uses integers to identify particular
    auxiliary data items.  But rather than declaring these as an integer type
-   directly, a typedef =auxid_t= is used.  This allows for the possibility
+   directly, an alias =auxid_t= is used.  This allows for the possibility
    of changing the type in the future without having to make changes
    throughout the code base.  It also makes explicit that variables
    of that type are meant to identify auxiliary data items,
    rather than being random integers.
 
-   A typedef may also be used inside a function body to shorten
+   An alias may also be used inside a function body to shorten
    a cumbersome type name; however, this should be used sparingly.
 
 
@@ -3008,6 +3125,20 @@ foo( 1, 2, 3 )
    fix the whole thing, or code to match.
 
 
+ - *Prefer* =using= *to* =typedef=*.* [<<prefer-using>>]
+
+   To declare a type alias, prefer the newer =using= syntax:
+   #+BEGIN_EXAMPLE
+using Int_t = int;
+#+END_EXAMPLE
+   to the =typedef= syntax:
+   #+BEGIN_EXAMPLE
+typedef int Int_t;
+#+END_EXAMPLE
+   The =using= syntax makes it clearer what is being defined; it can also
+   be used to declare templated aliases.
+
+
 ** Comments
 
  - *Use Doxygen style comments before class/method/data member declarations.  
@@ -3130,6 +3261,18 @@ The comment includes the fact that it is the perpendicular distance.
 
 ** Version 0.8
  - Updated for C++20.
+   - Don't use modules or coroutines.
+   - Add recommendation to use =<numbers>=.
+   - Suggest using =auto= to move the return type to the end of a method
+     signature when returning types defined within the class.
+   - Suggest not defining template functions without the =template= keyword.
+   - Recommend =std::format= for formatted output.
+   - Note that range-for can have init-statements.
+   - Mention =std::bit_cast=.
+   - Recommend =using= instead of =typedef=.  Rephrase previous references
+     to =typedef=.
+   - Comparisons should be defined in terms of =operator=== and =operator<=>=.
+   - Mention =std::span=.
  - Some additional references.
  - Clarify that non-ascii characters should not be used in identifier names.
  - Clarify that variable-length argument lists of varadic template functions