func Diff(shouldMatch M, butNot M) M {
	r16s := []unicode.Range16{}
	r32s := []unicode.Range32{}
	for _, rng := range shouldMatch.rt.R16 {
		for r := rng.Lo; r <= rng.Hi; r += rng.Stride {
			if !butNot.Matches(rune(r)) {
				r16s = append(r16s, unicode.Range16{r, r, 1})
			}
		}
	}
	for _, rng := range shouldMatch.rt.R32 {
		for r := rng.Lo; r <= rng.Hi; r += rng.Stride {
			if !butNot.Matches(rune(r)) {
				r32s = append(r32s, unicode.Range32{r, r, 1})
			}
		}
	}
	return M{
		rt: rangetable.Merge(&unicode.RangeTable{
			R16: r16s,
			R32: r32s,
		}),  
		desc: shouldMatch.String() + " but not (" + butNot.String() + ")",
	}
}
Diff returns a matcher that matches runes, that are matched by shouldMatch,
but are not matched by butNot.

Diff(A, B).Matches(r) => r element (A \ B)
package matcher
import (
	"strings"
	"unicode"
	"golang.org/x/text/unicode/rangetable"
)
type M struct {
	rt   *unicode.RangeTable
	desc string
}
func (m M) Matches(r rune) bool { return unicode.Is(m.rt, r) }
func (m M) String() string { return m.desc }
func New(desc string, rt *unicode.RangeTable) M {
	return M{rt, desc}
}
func Merge(ms ...M) M {
	var rtms []*unicode.RangeTable
	descs := make([]string, len(ms))
	for i, m := range ms {
		descs[i] = m.String()
		rtms = append(rtms, m.rt)
	}
	return M{
		rt:   rangetable.Merge(rtms...),
		desc: strings.Join(descs, " or "),
	}
}
func RuneWithDesc(desc string, exp rune) M {
	return M{
		rt:   rangetable.New(exp),
		desc: desc,
	}
}
func String(s string) M {
	return M{
		rt:   rangetable.New([]rune(s)...),
		desc: "one of '" + s + "'",
	}
}