static-assertions icon indicating copy to clipboard operation
static-assertions copied to clipboard

More Assertions

Open nvzqz opened this issue 8 years ago • 23 comments

Discussion of other possible static assertions should occur here.

nvzqz avatar Aug 13 '17 07:08 nvzqz

Asserting Sync and Sendness: http://yakshav.es/asserting-static-properties/

Also, I'm still searching for a way to statically assert that a trait is object-safe.

skade avatar Aug 13 '17 10:08 skade

@skade Just added assert_obj_safe, which is in release v0.2.1.

nvzqz avatar Aug 13 '17 15:08 nvzqz

Here's a sample definition of a macro that would assert certain traits are implemented.

macro_rules! assert_impl {
    ($x:ty, $($xs:ty),+) => {
        $({
            fn _gen<T: ?Sized + $xs>() {}
            _gen::<$x>();
        })+
    };
    ($label:ident; $($xs:ty),+) => {
        #[allow(dead_code, non_snake_case)]
        fn $label() { assert_impl!($($xs),+); }
    };
}

However, when trying to use it, I get this error:

error: expected one of `(`, `,`, `=`, `>`, `?`, `for`, lifetime, or path, found `Sync`
   --> src/lib.rs:330:33
    |
330 |             fn _gen<T: ?Sized + $xs>() {}
    |                                -^^^ unexpected token
    |                                |
    |                                expected one of 8 possible tokens here
...
340 | assert_impl!(byte; u8, Sync);
    | ----------------------------- in this macro invocation

nvzqz avatar Aug 13 '17 16:08 nvzqz

@skade Added assert_impl, which is in release v0.2.2 🎉

nvzqz avatar Aug 13 '17 20:08 nvzqz

Would be cool to see an assertion that traits are not implemented: assert_not_impl.

iliekturtles avatar Dec 21 '17 20:12 iliekturtles

There was some form of doing this that @kennytm recommend me. I don't exactly remember what it was.

nvzqz avatar Dec 21 '17 23:12 nvzqz

I would love to have something like

assert_str_startswith!(str, "and")

Usecase is a CLI-builder-pattern subcrate I am trying to build for imag.

matthiasbeyer avatar Jan 03 '18 11:01 matthiasbeyer

@matthiasbeyer This would likely have to be implemented as a procedural macro. In that case, both parameters would have to be string literals. I'm curious if this could be done via const fn in combination with const_assert!. 🤔

@iliekturtles keep an eye on #8 in case such a macro becomes implemented.

nvzqz avatar May 10 '18 08:05 nvzqz

What about a macro to assert that two types are equal?

jendrikw avatar Jul 06 '18 10:07 jendrikw

Could you add a macro that asserts that an enum variant exists?

robinkrahl avatar Feb 20 '19 12:02 robinkrahl

@jendrikw I finally decided to close #9 with 1f470d2a0d6197214a748481265625e6e678ec62.

It produces a rather nice error message:

error[E0308]: mismatched types
  --> tests/eq_ty.rs:12:5
   |
12 |     assert_eq_type!(byte; super::X, u8, usize, (super::X));
   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected u8, found usize

nvzqz avatar May 14 '19 21:05 nvzqz

Hi, I've struct and trait that require lifetime. My first though about using assert_impl_all is I need to specify the lifetime in precise manner:

assert_impl_all!(require;
	Transition<'t>,
	SemanticAnalyze<'t>,
	From<TokenPair<'t>>,
);

However, seems that's not supported. Rather I found traits need to be 'static lifetime:

assert_impl_all!(require;
	Transition,
	SemanticAnalyze<'static>,
	From<TokenPair<'static>>,
);

I think this should be anonymous '_ lifetime rather than 'static lifetime 🤔

This also give me an idea. What about macro for asserting lifetimes?


Ah yes, #![feature(underscore_const_names)] now has been stabilized 😁 https://blog.rust-lang.org/2019/08/15/Rust-1.37.0.html#using-unnamed-const-items-for-macros but in v0.3.4, it still require label despite using Rust-1.3.7 🤔

DrSensor avatar Aug 19 '19 23:08 DrSensor

@nvzqz is there an issue for asserting Unpin and !Unpin? I know we talked about this at rustconf :)

LucioFranco avatar Aug 30 '19 17:08 LucioFranco

@DrSensor the 1.0 beta is coming out soon. I'm dealing with life stuff which is why it hasn't come out yet. I tried getting around to it when 1.37 came out but I've had other priorities. Thanks for your patience!

@LucioFranco if you'd like to do the leg work to assert that the type of an expression implements given traits, please make a PR! To learn how to go about what we discussed, check out how assert_eq_size_val! and assert_{not,}_impl_{any,all}! are implemented. I'd be happy with doing it myself but this is a good opportunity to make a contribution I can mentor you on. 😄

nvzqz avatar Aug 30 '19 18:08 nvzqz

I am interested for a static assertion that some trait bound currently in scope is statically impossible.

Example:

#![feature(trivial_bounds)]

macro_rules! void {
    ($($tt:tt)*) => {
        unreachable!() // FIXME
    };
}

trait Trait {
    fn f() where Self: Sized;
}

impl Trait for str {
    fn f() where str: Sized {
        void!(str: Sized); // evaluate to !
    }
}

This assertion should require that both str: Sized is currently in scope (e.g. in the example let _s: str; does compile, where ordinarily it would not) and str: Sized is statically unsatisfiable.

dtolnay avatar Feb 15 '20 20:02 dtolnay

@dtolnay: here is a sad version of what you ask:

macro_rules! void {
    ($ty:ty: $tr:path) => {{
        struct True;
        struct False;
        trait DoesImpl<M> {
            fn marker() {}
        }
        impl<T: ?Sized> DoesImpl<False> for T {}
        impl<T: ?Sized + $tr> DoesImpl<True> for T {}

        const _: () = {
            // Fails if `$ty: $tr` is in scope globally, because then it can't infer `M`
            let _ = <$ty as DoesImpl<_>>::marker;
        };
        // Fails if `$ty: $tr` is not in scope locally
        let _ = <$ty as DoesImpl<True>>::marker;

        unreachable!()
    }};
}

It will only typecheck in the case where the type does not implement the trait, but the bound $ty: $tr is in scope anyways. I find this solution disappointing however: I'd love to be able to exploit the contradiction str: Sized + !Sized to construct a term of type !...

Nadrieril avatar Feb 17 '20 11:02 Nadrieril

That looks fine to me. Is it something that would be a good fit for this crate?

dtolnay avatar Feb 25 '20 00:02 dtolnay

It would be fantastic to have static assertions added for lifetime parameter covariance/contravariance/invariance, since changing it in a public API can be a semver breakage.

sunshowers avatar Apr 03 '20 22:04 sunshowers

Is less than/greater than possible? More specifically, I'd be interested in $len <= u8::max_value(). Ideally for u16 and larger too (so exhaustive 256 trait impls should not be used) but limiting it to u8 is already useful. Bonus points if it works in Rust 1.41.1.

Kixunil avatar Jan 29 '21 11:01 Kixunil

@Kixunil isn't that already possible with const_assert!(len <= u8::MAX) (as long as len is const)? Apart from that, I'm not sure how something is ever going to be bigger than the maximum value of that type...

konsumlamm avatar Aug 19 '21 11:08 konsumlamm

@konsumlamm frankly, I don't remember what I wanted to use it for but I believe $len in a macro was not of type u8.

Kixunil avatar Aug 19 '21 13:08 Kixunil

Hi, would an extension for lifetimes in the assertions be useful? I ran across an issue with traits that require lifetime parameters and have to manually write the assertion based on the expansion that the assert_* macros do.

HTGAzureX1212 avatar Nov 07 '21 07:11 HTGAzureX1212