syn icon indicating copy to clipboard operation
syn copied to clipboard

Why is Spanned sealed?

Open TedDriggs opened this issue 2 years ago • 3 comments

I saw that syn 2.0 seals the Spanned trait (and that it was previously also sealed in #439). This is an issue for my crate darling, which has some structs that impl Spanned but don’t impl ToTokens.

I could come up with a workaround, such as creating darling::Spanned with a blanket impl to the syn one, but I’d like to understand why the trait was sealed so that my workaround isn’t somehow flawed/doomed to failure.

TedDriggs avatar Apr 06 '23 12:04 TedDriggs

Additionally, this doesn't seem to be documented in the release notes for syn 2, in the list of breaking changes.

I see that Spanned is still implemented for every quote::ToTokens. In syn 2 with the sealed Spanned trait, this has required:

impl<T: ?Sized + ToTokens> Sealed for T {}

Previously, in syn 1.0, this was achieved in babcfc6a079539757ecd57862092e2d6e7f6b940 "Use quote's Spanned to provide impl for Span". That commit unseals Spanned.

The situation with syn 2 is rather strange, now. Spanned isn't really sealed: you can always impl Spanned: all you have to do is impl ToTokens too, since ToTokens isn't sealed. But of course your type might not really be ToTokens so you might have to make your ToTokens impl broken (you could make it panic, for example).

(As an example of a type that could be Spanned but not ToTokens: anything that can be represented as tokens but only fallibly.)

I can't see any logical reason why the combination "Spanned but not ToTokens" is particularly hazardous.

ijackson avatar Jul 09 '23 09:07 ijackson

In general, I am finding something similar with a lot of the traits that are Sealed, or structs/functionality that is hidden as it is marked private.

What exactly is syn protecting us from? Is it a way to make the public API safer? As it is, I often end up re-implementing something that is similar to what I see in the syn source code.

bzm3r avatar Jul 14 '23 17:07 bzm3r

I proposed to unseal this in #1524. @dtolnay has rejected that MR. Accordingly, I think people who want Spanned for their own non-ToTokens types may need to invent a local Spanned trait, or something.

ijackson avatar Oct 30 '23 16:10 ijackson

That's right, using your own alternative trait is the way to go. For those use cases, bear in mind that a -> Span signature is not what you would want anyway.

dtolnay avatar Apr 17 '24 20:04 dtolnay

Why isn’t that signature what we want in most cases?

TedDriggs avatar Apr 17 '24 21:04 TedDriggs

I touched on this in the linked PR in https://github.com/dtolnay/syn/pull/1524#pullrequestreview-1704425166. In stable Rust, a Span always refers to exactly 1 single token. So a function that returns Span is not useful for anything that intends to refer to a whole syntax tree node or multiple tokens. For example this is why syn::Error::new_spanned is not built on a trait that returns Span.

dtolnay avatar Apr 17 '24 22:04 dtolnay