I believe that they are simply two different ways to define the same thing.
I haven’t seen any article/definition which even gives a hint that the two could be functionally different in any way, but I’ve not looked everywhere.
In the code which I have read, it seems that the attribute is more-commonly used when other attributes are used, but I don’t think that’s a rule which needs to be adhered to.
I would be happy to learn that I am wrong about this.
I think t3 is officially a “record” that happens to be a struct, while t1 is not a record, and behaves the same as a struct in C#. I think t3 is the more idiomatic one you’ll see in more community F# code. Obviously, it’s much less verbose than t1. Other differences I’m aware of include how you construct them, where t3 will be constructed with the “normal” record syntax: { x = 1; y = 3 } while t1 will use a constructor function instead t1(1, 3). t3 gets the record “copy-and-update” feature, where you can do { myT3 with x = 4 }. I think with t3 you can control equality and comparison with [<CustomEquality>], [<CustomComparison>], [<NoEquality>], [<NoComparison>], [<EqualityConditionalOn>] and friends, and I don’t think you can do the same with t1. There might be other record features that t3 has that t1 doesn’t, but I’m not aware of others.
If I were to guess, the t1 syntax exists because that was the only way to create structs prior to the [<struct>] tag being introduced in F# 4.1
I think you already know this, but t2 is a very different animal from t1 and t3, as it’s an object instead of a struct. That means its fields are heap allocated instead of stack allocated. That can either help or hurt your performance based on usage, and can be a little tricky to figure out which is better in a given situation.