Skip to content

Moving the reference lifetime at the trait level #375

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 2 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 2 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -156,9 +156,7 @@ struct MyEqMatcher<T> {
expected: T,
}

impl<T: PartialEq + Debug> Matcher for MyEqMatcher<T> {
type ActualT = T;

impl<T: PartialEq + Debug> Matcher<T> for MyEqMatcher<T> {
fn matches(&self, actual: &Self::ActualT) -> MatcherResult {
(self.expected == *actual).into()
}
Expand All @@ -179,7 +177,7 @@ impl<T: PartialEq + Debug> Matcher for MyEqMatcher<T> {
It is recommended to expose a function which constructs the matcher:

```rust
pub fn eq_my_way<T: PartialEq + Debug>(expected: T) -> impl Matcher<ActualT = T> {
pub fn eq_my_way<T: PartialEq + Debug>(expected: T) -> impl Matcher<T> {
MyEqMatcher { expected }
}
```
Expand Down
36 changes: 16 additions & 20 deletions googletest/crate_docs.md
Original file line number Diff line number Diff line change
Expand Up @@ -206,20 +206,19 @@ The following matchers are provided in GoogleTest Rust:

One can extend the library by writing additional matchers. To do so, create
a struct holding the matcher's data and have it implement the trait
[`Matcher`]:
[`Matcher`] (and optionally the [`MatcherExt`] trait):

```no_run
use googletest::{description::Description, matcher::{Matcher, MatcherResult}};
use googletest::{description::Description, matcher::{Matcher, MatcherExt, MatcherResult}};
use std::fmt::Debug;

#[derive(MatcherExt)]
struct MyEqMatcher<T> {
expected: T,
}

impl<T: PartialEq + Debug> Matcher for MyEqMatcher<T> {
type ActualT = T;

fn matches(&self, actual: &Self::ActualT) -> MatcherResult {
impl<'a, T: PartialEq + Debug> Matcher<'a, T> for MyEqMatcher<T> {
fn matches<'b>(&self, actual: &'b T) -> MatcherResult where 'a: 'b{
if self.expected == *actual {
MatcherResult::Match
} else {
Expand All @@ -243,17 +242,15 @@ impl<T: PartialEq + Debug> Matcher for MyEqMatcher<T> {
It is recommended to expose a function which constructs the matcher:

```no_run
# use googletest::{description::Description, matcher::{Matcher, MatcherResult}};
# use googletest::{description::Description, matcher::{Matcher, MatcherExt, MatcherResult}};
# use std::fmt::Debug;
#
# #[derive(MatcherExt)]
# struct MyEqMatcher<T> {
# expected: T,
# }
#
# impl<T: PartialEq + Debug> Matcher for MyEqMatcher<T> {
# type ActualT = T;
#
# fn matches(&self, actual: &Self::ActualT) -> MatcherResult {
# impl<'a, T: PartialEq + Debug> Matcher<'a, T> for MyEqMatcher<T> {
# fn matches<'b>(&self, actual: &'b T) -> MatcherResult where 'a: 'b{
# if self.expected == *actual {
# MatcherResult::Match
# } else {
Expand All @@ -273,7 +270,7 @@ impl<T: PartialEq + Debug> Matcher for MyEqMatcher<T> {
# }
# }
#
pub fn eq_my_way<T: PartialEq + Debug>(expected: T) -> impl Matcher<ActualT = T> {
pub fn eq_my_way<'a, T: PartialEq + Debug>(expected: T) -> impl Matcher<'a, T> {
MyEqMatcher { expected }
}
```
Expand All @@ -282,17 +279,15 @@ impl<T: PartialEq + Debug> Matcher for MyEqMatcher<T> {

```
# use googletest::prelude::*;
# use googletest::{description::Description, matcher::{Matcher, MatcherResult}};
# use googletest::{description::Description, matcher::{Matcher, MatcherExt, MatcherResult}};
# use std::fmt::Debug;
#
# #[derive(MatcherExt)]
# struct MyEqMatcher<T> {
# expected: T,
# }
#
# impl<T: PartialEq + Debug> Matcher for MyEqMatcher<T> {
# type ActualT = T;
#
# fn matches(&self, actual: &Self::ActualT) -> MatcherResult {
# impl<'a, T: PartialEq + Debug> Matcher<'a, T> for MyEqMatcher<T> {
# fn matches<'b>(&self, actual: &'b T) -> MatcherResult where 'a: 'b{
# if self.expected == *actual {
# MatcherResult::Match
# } else {
Expand All @@ -312,7 +307,7 @@ impl<T: PartialEq + Debug> Matcher for MyEqMatcher<T> {
# }
# }
#
# pub fn eq_my_way<T: PartialEq + Debug>(expected: T) -> impl Matcher<ActualT = T> {
# pub fn eq_my_way<'a, T: PartialEq + Debug>(expected: T) -> impl Matcher<'a, T> {
# MyEqMatcher { expected }
# }
# /* The attribute macro would prevent the function from being compiled in a doctest.
Expand Down Expand Up @@ -493,3 +488,4 @@ through the `?` operator as the example above shows.
[`and_log_failure()`]: GoogleTestSupport::and_log_failure
[`into_test_result()`]: IntoTestResult::into_test_result
[`Matcher`]: matcher::Matcher
[`MatcherExt`]: matcher::MatcherExt
6 changes: 3 additions & 3 deletions googletest/src/assertions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -497,9 +497,9 @@ pub mod internal {
///
/// **For internal use only. API stablility is not guaranteed!**
#[must_use = "The assertion result must be evaluated to affect the test result."]
pub fn check_matcher<T: Debug + ?Sized>(
actual: &T,
expected: impl Matcher<ActualT = T>,
pub fn check_matcher<'a, T: Debug + ?Sized>(
actual: &'a T,
expected: impl Matcher<'a, T>,
actual_expr: &'static str,
source_location: SourceLocation,
) -> Result<(), TestAssertionFailure> {
Expand Down
2 changes: 1 addition & 1 deletion googletest/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ pub mod matchers;
/// }
/// ```
pub mod prelude {
pub use super::matcher::Matcher;
pub use super::matcher::{Matcher, MatcherExt};
pub use super::matchers::*;
pub use super::verify_current_test_outcome;
pub use super::GoogleTestSupport;
Expand Down
46 changes: 25 additions & 21 deletions googletest/src/matcher.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,24 +19,22 @@ use crate::internal::source_location::SourceLocation;
use crate::internal::test_outcome::TestAssertionFailure;
use crate::matchers::__internal_unstable_do_not_depend_on_these::ConjunctionMatcher;
use crate::matchers::__internal_unstable_do_not_depend_on_these::DisjunctionMatcher;
pub use googletest_macro::MatcherExt;
use std::fmt::Debug;

/// An interface for checking an arbitrary condition on a datum.
///
/// This trait is automatically implemented for a reference of any type
/// implementing `Matcher`. This simplifies reusing a matcher in different
/// assertions.
pub trait Matcher {
/// The type against which this matcher matches.
type ActualT: Debug + ?Sized;

pub trait Matcher<'a, ActualT: Debug + ?Sized> {
/// Returns whether the condition matches the datum `actual`.
///
/// The trait implementation defines what it means to "match". Often the
/// matching condition is based on data stored in the matcher. For example,
/// `eq` matches when its stored expected value is equal (in the sense of
/// the `==` operator) to the value `actual`.
fn matches(&self, actual: &Self::ActualT) -> MatcherResult;
fn matches<'b>(&self, actual: &'b ActualT) -> MatcherResult where 'a: 'b;

/// Returns a description of `self` or a negative description if
/// `matcher_result` is `DoesNotMatch`.
Expand Down Expand Up @@ -137,10 +135,22 @@ pub trait Matcher {
/// .nested(self.expected.explain_match(actual.deref()))
/// }
/// ```
fn explain_match(&self, actual: &Self::ActualT) -> Description {
fn explain_match<'b>(&self, actual: &'b ActualT) -> Description where 'a: 'b{
format!("which {}", self.describe(self.matches(actual))).into()
}
}

/// Trait extension for matchers. It is highly recommended to implement it for
/// each type implementing `Matcher`.
// The `and` and `or` functions cannot be part of the `Matcher` traits since it
// is parametric. Consider that `and` and `or` are part of the `Matcher` trait
// and `MyMatcher` implements both `Matcher<A>` and `Matcher<B>`.
// Then `MyMatcher{...}.and(...)` can be either:
// * `Matcher::<A>::and(MyMatcher{...}, ...)` or
// * `Matcher::<B>::and(MyMatcher{...}, ...)`.
// Moving the `and` and `or` functions in a non-generic trait remove this
// confusion by making `and` and `or` unique for a given type.
pub trait MatcherExt {
/// Constructs a matcher that matches both `self` and `right`.
///
/// ```
Expand All @@ -164,10 +174,7 @@ pub trait Matcher {
// TODO(b/264518763): Replace the return type with impl Matcher and reduce
// visibility of ConjunctionMatcher once impl in return position in trait
// methods is stable.
fn and<Right: Matcher<ActualT = Self::ActualT>>(
self,
right: Right,
) -> ConjunctionMatcher<Self, Right>
fn and<Right>(self, right: Right) -> ConjunctionMatcher<Self, Right>
where
Self: Sized,
{
Expand All @@ -194,10 +201,7 @@ pub trait Matcher {
// TODO(b/264518763): Replace the return type with impl Matcher and reduce
// visibility of DisjunctionMatcher once impl in return position in trait
// methods is stable.
fn or<Right: Matcher<ActualT = Self::ActualT>>(
self,
right: Right,
) -> DisjunctionMatcher<Self, Right>
fn or<Right>(self, right: Right) -> DisjunctionMatcher<Self, Right>
where
Self: Sized,
{
Expand All @@ -214,9 +218,9 @@ const PRETTY_PRINT_LENGTH_THRESHOLD: usize = 60;
///
/// The parameter `actual_expr` contains the expression which was evaluated to
/// obtain `actual`.
pub(crate) fn create_assertion_failure<T: Debug + ?Sized>(
matcher: &impl Matcher<ActualT = T>,
actual: &T,
pub(crate) fn create_assertion_failure<'a, T: Debug + ?Sized>(
matcher: &impl Matcher<'a, T>,
actual: &'a T,
actual_expr: &'static str,
source_location: SourceLocation,
) -> TestAssertionFailure {
Expand Down Expand Up @@ -273,18 +277,18 @@ impl MatcherResult {
}
}

impl<M: Matcher> Matcher for &M {
type ActualT = M::ActualT;
impl<M: ?Sized + MatcherExt> MatcherExt for &M {}

fn matches(&self, actual: &Self::ActualT) -> MatcherResult {
impl<'a, T: Debug + ?Sized, M: Matcher<'a, T>> Matcher<'a, T> for &M {
fn matches<'b>(&self, actual: &'b T) -> MatcherResult where 'a : 'b{
(*self).matches(actual)
}

fn describe(&self, matcher_result: MatcherResult) -> Description {
(*self).describe(matcher_result)
}

fn explain_match(&self, actual: &Self::ActualT) -> Description {
fn explain_match<'b>(&self, actual: &'b T) -> Description where 'a: 'b{
(*self).explain_match(actual)
}
}
Expand Down
12 changes: 6 additions & 6 deletions googletest/src/matchers/all_matcher.rs
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@
/// ```
///
/// Using this macro is equivalent to using the
/// [`and`][crate::matcher::Matcher::and] method:
/// [`and`][crate::matcher::MatcherExt::and] method:
///
/// ```
/// # use googletest::prelude::*;
Expand Down Expand Up @@ -78,12 +78,12 @@ mod tests {

#[test]
fn description_shows_more_than_one_matcher() -> Result<()> {
let first_matcher: StrMatcher<String, _> = starts_with("A");
let first_matcher = starts_with("A");
let second_matcher = ends_with("string");
let matcher = all!(first_matcher, second_matcher);

verify_that!(
matcher.describe(MatcherResult::Match),
Matcher::<String>::describe(&matcher, MatcherResult::Match),
displays_as(eq(indoc!(
"
has all the following properties:
Expand All @@ -95,19 +95,19 @@ mod tests {

#[test]
fn description_shows_one_matcher_directly() -> Result<()> {
let first_matcher: StrMatcher<String, _> = starts_with("A");
let first_matcher = starts_with("A");
let matcher = all!(first_matcher);

verify_that!(
matcher.describe(MatcherResult::Match),
Matcher::<String>::describe(&matcher, MatcherResult::Match),
displays_as(eq("starts with prefix \"A\""))
)
}

#[test]
fn mismatch_description_shows_which_matcher_failed_if_more_than_one_constituent() -> Result<()>
{
let first_matcher: StrMatcher<str, _> = starts_with("Another");
let first_matcher = starts_with("Another");
let second_matcher = ends_with("string");
let matcher = all!(first_matcher, second_matcher);

Expand Down
10 changes: 5 additions & 5 deletions googletest/src/matchers/any_matcher.rs
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@
/// ```
///
/// Using this macro is equivalent to using the
/// [`or`][crate::matcher::Matcher::or] method:
/// [`or`][crate::matcher::MatcherExt::or] method:
///
/// ```
/// # use googletest::prelude::*;
Expand Down Expand Up @@ -80,12 +80,12 @@ mod tests {

#[test]
fn description_shows_more_than_one_matcher() -> Result<()> {
let first_matcher: StrMatcher<String, &str> = starts_with("A");
let first_matcher = starts_with("A");
let second_matcher = ends_with("string");
let matcher = any!(first_matcher, second_matcher);

verify_that!(
matcher.describe(MatcherResult::Match),
Matcher::<String>::describe(&matcher, MatcherResult::Match),
displays_as(eq(indoc!(
"
has at least one of the following properties:
Expand All @@ -97,11 +97,11 @@ mod tests {

#[test]
fn description_shows_one_matcher_directly() -> Result<()> {
let first_matcher: StrMatcher<String, &str> = starts_with("A");
let first_matcher = starts_with("A");
let matcher = any!(first_matcher);

verify_that!(
matcher.describe(MatcherResult::Match),
Matcher::<String>::describe(&matcher, MatcherResult::Match),
displays_as(eq("starts with prefix \"A\""))
)
}
Expand Down
20 changes: 11 additions & 9 deletions googletest/src/matchers/anything_matcher.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,9 @@

use crate::{
description::Description,
matcher::{Matcher, MatcherResult},
matcher::{Matcher, MatcherExt, MatcherResult},
};
use std::{fmt::Debug, marker::PhantomData};
use std::fmt::Debug;

/// Matches anything. This matcher always succeeds.
///
Expand All @@ -32,16 +32,18 @@ use std::{fmt::Debug, marker::PhantomData};
/// # }
/// # should_pass().unwrap();
/// ```
pub fn anything<T: Debug + ?Sized>() -> impl Matcher<ActualT = T> {
Anything::<T>(Default::default())
pub fn anything() -> Anything {
Anything
}

struct Anything<T: ?Sized>(PhantomData<T>);
#[derive(MatcherExt)]
pub struct Anything;

impl<T: Debug + ?Sized> Matcher for Anything<T> {
type ActualT = T;

fn matches(&self, _: &T) -> MatcherResult {
impl<'a, T: Debug + ?Sized> Matcher<'a, T> for Anything {
fn matches<'b>(&self, _: &'b T) -> MatcherResult
where
'a: 'b,
{
MatcherResult::Match
}

Expand Down
Loading