Rust: 'impl Trait'에 대한 추적 문제(RFC 1522, RFC 1951, RFC 2071)

에 만든 2016년 06월 27일  ·  417코멘트  ·  출처: rust-lang/rust

새로운 추적 문제 = https://github.com/rust-lang/rust/issues/63066

시행 현황

RFC 1522에 명시된 기본 기능이 구현되었지만 여전히 작업이 필요한 수정 사항이 있습니다.

RFC

impl 특성과 관련하여 많은 RFC가 있었으며 모두 이 중앙 추적 문제에 의해 추적되었습니다.

해결되지 않은 질문

구현은 다음과 같은 여러 흥미로운 질문도 제기했습니다.

B-RFC-implemented B-unstable C-tracking-issue T-lang disposition-merge finished-final-comment-period

가장 유용한 댓글

FCP가 종료되기 전 마지막 기회이기 때문에 자동 자동 특성에 대한 마지막 주장을 하고 싶습니다. 이것이 마지막 순간이라는 것을 알고 있으므로 기껏해야 현재 구현을 커밋하기 전에 이 문제를 공식적으로 해결하고 싶습니다.

impl Trait 을(를) 팔로우하지 않은 사람을 위해 명확히 하기 위해 이것이 제가 제시하는 문제입니다. impl X 유형으로 표현되는 유형은 현재 자동 특성을 자동으로 구현하며 그 뒤에 있는 구체적인 유형이 해당 자동 특성을 구현하는 경우에만 해당합니다. 구체적으로, 다음 코드가 변경되면 함수는 계속 컴파일되지만 반환하는 형식이 Send 구현한다는 사실에 의존하는 함수의 사용은 실패합니다.

 fn does_some_operation() -> impl Future<Item=(), Error=()> {
-    let data_stored = Arc::new("hello");
+    let data_stored = Rc::new("hello");

     return some_long_operation.and_then(|other_stuff| {
         do_other_calculation_with(data_stored)
     });
}

(간단한 예: 작동 중 , 내부 변경으로 인해 실패 )

이 문제는 명확하지 않습니다. 자동 특성을 "누출"시키려는 매우 고의적인 결정이 있었습니다. 그렇게 하지 않으면 전송되지 않거나 동기화되지 않은 것을 반환하는 모든 함수에 + !Send + !Sync 를 넣어야 합니다. 함수가 반환하는 구체적인 유형에서 단순히 구현할 수 없는 잠재적인 다른 사용자 정의 자동 특성에 대한 불명확한 이야기가 있습니다. 이 두 가지 문제는 나중에 다루겠습니다.

먼저 이 문제에 대한 반대 의견을 간단히 말씀드리고 싶습니다. 이렇게 하면 함수 본문을 변경하여 공개 API를 변경할 수 있습니다. 이것은 코드의 유지보수성을 직접적으로 감소시킵니다.

녹이 개발되는 동안 유용성보다 장황한 측면에서 잘못된 결정이 내려졌습니다. 새로 온 사람들이 이것을 볼 때 그들은 그것이 장황함을 위한 장황한 것이라고 생각하지만 이것은 사실이 아닙니다. 구조가 자동으로 Copy를 구현하지 않도록 할지 또는 함수 서명에서 모든 유형을 명시적으로 만들지 여부에 관계없이 각 결정은 유지 관리를 위한 것입니다.

사람들에게 Rust를 소개하면 확실히 속도, 생산성, 메모리 안전성을 보여줄 수 있습니다. 그러나 이동에는 속도가 있습니다. Ada는 메모리 안전성이 있습니다. 파이썬에는 생산성이 있습니다. Rust가 이 모든 것을 능가하는 것은 유지 관리가 용이합니다. 라이브러리 작성자가 알고리즘을 보다 효율적으로 변경하려는 경우 또는 크레이트 구조를 다시 실행하려는 경우 컴파일러로부터 실수를 했을 때 알려주는 강력한 보증을 받습니다. 내 코드가 메모리 안전뿐만 아니라 논리 및 인터페이스 측면에서도 계속 작동할 것이라고 확신할 수 있습니다. _Rust의 모든 함수 인터페이스는 함수의 유형 선언으로 완전히 표현할 수 있습니다_.

impl Trait 그대로 안정화하는 것은 이러한 믿음에 어긋날 가능성이 큽니다. 물론, 코드를 빠르게 작성하는 것은 매우 좋지만 프로토타입을 만들고 싶다면 파이썬을 사용할 것입니다. Rust는 단기 쓰기 전용 코드가 아닌 장기 유지 관리가 필요할 때 선택하는 언어입니다.


다시 말하지만, 문제가 명확하지 않기 때문에 이것이 나쁠 가능성이 크다고 말합니다. 우선 '자동 특성'에 대한 전체 아이디어는 명확하지 않습니다. Send 및 Sync는 공개 선언이 아닌 구조체의 내용을 기반으로 구현됩니다. 이 결정은 녹슬지 않았으므로 impl Trait 도 비슷하게 작동할 수 있습니다.

그러나 함수와 구조는 코드베이스에서 다르게 사용되며 동일한 문제가 아닙니다.

구조의 필드를 수정할 때, 심지어 private 필드라도 실제 내용을 변경하고 있음을 즉시 알 수 있습니다. non-Send 또는 non-Sync 필드가 있는 구조가 그 선택을 했으며 라이브러리 유지 관리자는 PR이 구조의 필드를 변경할 때 다시 확인하는 것을 알고 있습니다.

함수의 내부를 수정할 때 성능과 정확성 모두에 영향을 미칠 수 있다는 것은 분명합니다. 그러나 Rust에서는 올바른 유형을 반환하는지 확인할 필요가 없습니다. 함수 선언은 우리가 지켜야 하는 엄격한 계약이며 rustc 는 우리를 지켜줍니다. 그것은 구조체의 자동 특성과 함수 반환 사이의 가는 선이지만, 함수의 내부를 변경하는 것은 훨씬 더 일상적입니다. 완전한 발전기 구동 Future 가 있으면 -> impl Future 반환하는 함수를 수정하는 것이 훨씬 더 일상적일 것입니다. 컴파일러가 이를 포착하지 못하는 경우 작성자가 수정된 전송/동기화 구현을 선별해야 하는 모든 변경 사항입니다.

이 문제를 해결하기 위해 원래 RFC 토론 에서 보수적인 impl 특성 RFC의 이 섹션은 자동 특성 누출에 대한 가장 큰 주장을 제시합니다("OIBIT"는 auto 특성의 이전 이름임).

나는 이미 이에 대한 나의 주요 답변을 제시했지만, 여기에 마지막 메모가 있습니다. 구조의 레이아웃을 변경하는 것은 그리 일반적이지 않습니다. 방지할 수 있습니다. 기능이 훨씬 더 많이 변경되기 때문에 기능이 동일한 자동 특성을 계속 구현하도록 하는 유지 관리 부담이 구조의 부담보다 큽니다.


마지막으로, 자동 자동 특성이 유일한 옵션은 아니라는 점을 말씀드리고 싶습니다. 우리가 선택한 옵션이지만 옵트 아웃 자동 특성의 대안은 여전히 ​​대안입니다.

+ !Send + !Sync 상태로 보내기/비동기화 항목을 반환하거나 해당 경계가 있는 특성(별칭?)을 반환하는 함수가 필요할 수 있습니다. 이것은 좋은 결정은 아니지만 현재 선택하는 것보다 나을 수 있습니다.

사용자 정의 자동 특성에 대한 우려에 관해서는 자동 특성 이후에 도입된 새로운 유형에 대해서만 새로운 자동 특성을 구현하지 않아야 한다고 주장합니다. 이것은 내가 지금 해결할 수 있는 것보다 더 많은 문제를 제공할 수 있지만 더 많은 디자인으로 해결할 수 없는 문제는 아닙니다.


이것은 매우 늦고 매우 오랜 시간이 걸렸습니다. 저는 이전에 이러한 반대 의견을 제기한 적이 있다고 확신합니다. 마지막으로 한 번만 논평할 수 있게 되어 기쁩니다. 그리고 우리가 내리는 결정에 전적으로 동의합니다.

읽어주셔서 감사합니다. 최종 결정이 Rust가 나아갈 수 있는 최선의 방향이 되기를 바랍니다.

모든 417 댓글

@aturon 실제로 RFC를 저장소에 넣을 수 있습니까? ( @mbrubeck 은 이것이 문제 라고 언급했습니다 .)

완료.

첫 번째 구현 시도는 #35091입니다(두 번째, 작년의 내 지점 을 계산하는 경우).

내가 만난 한 가지 문제는 수명에 관한 것입니다. 유형 추론은 영역 변수를 _모든 곳에_ 넣는 것을 좋아하며 영역 검사 변경 없이 해당 변수는 로컬 범위 이외의 것을 추론하지 않습니다.
그러나 구체적인 유형은 _반드시_ 내보낼 수 있어야 하므로 'static 하고 명시적으로 초기 바인딩 수명 매개변수의 이름을 지정했습니다. 'static , 거의 완전히 쓸모가 없습니다.

내가 생각한 한 가지는 지역 검사 자체에 영향을 미치지 않는다는 것입니다. 수명을 지우는 것입니다.

  • impl Trait 의 구체적인 유형을 노출하는 것은 수명에 대해 신경 쓰지 않아야 합니다. Reveal::All 대한 빠른 검색은 컴파일러에서 이미 그러한 경우임을 시사합니다.
  • 경계는 함수의 반환 유형에서 impl Trait 의 모든 구체적인 유형에 배치되어야 해당 함수의 호출보다 오래 지속됩니다. 즉, 모든 수명은 필요에 따라 'static 또는 함수의 수명 매개변수 중 하나 - _even_ 무엇을 알 수 없는 경우(예: " 'a'b 중 가장 짧은 값")
  • impl Trait 의 암시적 수명 매개변수에 대한 분산을 선택해야 합니다(즉, 범위의 모든 수명 매개변수에 대해 유형 매개변수와 동일): 불변이 가장 쉽고 호출 수신자에게 더 많은 제어를 제공하는 반면 반공은 호출자가 수행할 수 있습니다. 더 많고 반환 유형의 모든 수명이 반공변 위치에 있는지 확인해야 합니다(불변 대신 공변 유형 매개변수와 동일).
  • 자동 특성 누출 메커니즘은 특성 경계가 다른 함수에서 구체적인 유형에 놓일 수 있음을 요구합니다. 수명을 지웠고 수명이 어디로 가는지 모르기 때문에 구체적인 유형에서 지워진 모든 수명은 대체되어야 합니다. 모든 실제 수명 매개변수 중에서 가장 짧은 수명보다 짧지 않다고 보장되는 새로운 추론 변수로; 문제는 특성 impls가 결국 더 강력한 평생 관계(예: X<'a, 'a> 또는 X<'static> )를 요구할 수 있다는 사실에 있습니다. 평생

자동 특성 누출에 대한 마지막 요점은 내 유일한 걱정거리이고 다른 모든 것은 간단해 보입니다.
이 시점에서 얼마나 많은 지역 검사를 그대로 재사용할 수 있는지 완전히 명확하지 않습니다. 바라건대 모두.

cc @rust-lang/lang

@eddyb

그러나 수명은 impl Trait _중요합니다_ - 예

fn get_debug_str(s: &str) -> impl fmt::Debug {
    s
}

fn get_debug_string(s: &str) -> impl fmt::Debug {
    s.to_string()
}

fn good(s: &str) -> Box<fmt::Debug+'static> {
    // if this does not compile, that would be quite annoying
    Box::new(get_debug_string())
}

fn bad(s: &str) -> Box<fmt::Debug+'static> {
    // if this *does* compile, we have a problem
    Box::new(get_debug_str())
}

RFC 스레드에서 여러 번 언급했습니다.

특성 객체 없는 버전:

fn as_debug(s: &str) -> impl fmt::Debug;

fn example() {
    let mut s = String::new("hello");
    let debug = as_debug(&s);
    s.truncate(0);
    println!("{:?}", debug);
}

이것은 as_debug 의 정의에 따라 UB이거나 그렇지 않습니다.

@arielb1 아, 맞아요. 제가 한 일 중 하나는 실제로 작동하지 않는다는 점을 제외하고는 익명의 늦은 바인딩이 아닌 평생 매개변수만 캡처하기

@arielb1 구체적인 유형 사전 삭제에서 발견되는 수명과 서명에서 늦게 바인딩된 수명 사이에 둘 수 있는 엄격한 수명 관계가 있습니까? 그렇지 않으면 평생 관계를 살펴보고 'a'static 또는 평생 매개변수가 아닌 _모든 것_인 직접 또는 _간접_ 'a outlives 'b 인스타 실패하는 것은 나쁜 생각이 아닐 수 있습니다. 'bimpl Trait 의 구체적인 유형에 나타납니다.

시간을 내어 여기에 다시 글을 써서 죄송합니다. 그래서 나는 생각했다
이 문제에 대해. 내 느낌은 우리가 궁극적으로 (그리고
하고 싶다) 새로운 종류의 제약으로 regionck를 확장한다 -- 나는 그것을 부를 것이다.
\in 제약 조건은 '0 \in {'a, 'b, 'c} 와 같이 말할 수 있기 때문에 '0 사용되는 영역은 다음과 같아야 합니다.
'a , 'b 또는 'c . 통합하는 가장 좋은 방법이 확실하지 않습니다.
이것은 스스로 해결하기 위해 -- 확실히 \in 세트가 싱글톤인 경우
세트, 그것은 단지 동등 관계입니다(현재 우리가 가지고 있지 않은
일류의 것이지만 두 가지 범위에서 구성될 수 있음), 그러나
그렇지 않으면 일이 복잡해집니다.

이것은 모두 지역 제약 조건 세트를 만들고자 하는 나의 욕망과 관련이 있습니다.
오늘날 우리가 가지고 있는 것보다 더 표현력이 있습니다. 확실히 하나는 작곡할 수 있습니다
\in OR== 제약 조건 중
표현 제약은 풀기 더 어렵고 \in 도 다르지 않습니다.

여하튼, 여기에서 나의 생각을 조금 끄적거려 보자. 이걸로 작업하자
예시:

pub fn foo<'a,'b>(x: &'a [u32], y: &'b [u32]) -> impl Iterator<Item=u32> {...}

impl Trait 대한 가장 정확한 디슈가링은 아마도
새로운 유형:

pub struct FooReturn<'a, 'b> {
    field: XXX // for some suitable type XXX
}

impl<'a,'b> Iterator for FooReturn<'a,'b> {
    type Item = <XXX as Iterator>::Item;
}

이제 impl Iterator<Item=u32>foo 는 다음과 동일하게 작동해야 합니다.
FooReturn<'a,'b> 가 작동합니다. 그렇다고 완벽한 궁합은 아니다. 하나
예를 들어 차이점은 eddyb가 언급한 것처럼 분산입니다.
impl Foo 와 같은 유형을 유형에 대해 불변으로 만든다고 가정합니다.
foo 매개변수 그러나 자동 특성 동작은 작동합니다.
(매치가 이상적이지 않을 수 있는 또 다른 영역은
impl Iterator 추상화를 "관통"하는 기능, 따라서 해당 코드
"내부" 추상화는 정확한 유형을 알고 있습니다. 그러면 정렬됩니다.
암시적 "풀기" 작업이 발생합니다.)

어떤 면에서 더 나은 일치는 일종의 합성 형질을 고려하는 것입니다.

trait FooReturn<'a,'b> {
    type Type: Iterator<Item=u32>;
}

impl<'a,'b> FooReturn<'a,'b> for () {
    type Type = XXX;
}

이제 impl Iterator 유형이 <() as FooReturn<'a,'b>>::Type 와 같다고 생각할 수 있습니다. 이것은 또한 완벽한 일치가 아닙니다. 왜냐하면 우리는
일반적으로 그것을 정상화할 것입니다. 전문화를 사용하는 것을 상상할 수 있습니다.
그것을 방지하기 위해:

trait FooReturn<'a,'b> {
    type Type: Iterator<Item=u32>;
}

impl<'a,'b> FooReturn<'a,'b> for () {
    default type Type = XXX; // can't really be specialized, but wev
}

이 경우 <() as FooReturn<'a,'b>>::Type 는 정규화되지 않으며,
그리고 우리는 훨씬 더 가까운 경기를 가지고 있습니다. 특히 분산은 다음과 같이 행동합니다.
오른쪽; "내부"에 있는 유형을 원했다면
추상화, 그들은 동일하지만 그들은
정상화하다. 그러나 catch가 있습니다. 자동 특성 항목은
꽤 일. (우리는 여기에서 조화를 고려하고 싶을 수도 있습니다.
실제로.)

어쨌든, 이러한 잠재적인 디슈가링을 탐구하는 나의 요점은
_actual_ desugaring으로 "impl Trait"을 구현하는 것이 좋습니다.
(좋을지 모르지만...) 하지만 우리 직업에 대한 직관을 주기 위해서입니다. 나
예측의 관점에서 두 번째 디슈가링은
우리를 앞으로 인도하는 데 꽤 도움이됩니다.

이 프로젝션 디슈가링이 정말 유용한 가이드 중 하나는
"살아남는" 관계. <() as FooReturn<'a,'b>>::Type: 'x 여부를 확인하려면 RFC 1214에서 이를 증명할 수 있다고 알려줍니다.
'a: 'x _and_ 'b: 'x 유지되는 한. 이것이 우리가 원하는 방식이라고 생각합니다.
impl 특성에 대해서도 처리합니다.

트랜스 시간 및 자동 특성의 경우 XXX 무엇인지 알아야 합니다.
물론입니다. 여기에서 기본 아이디어는 유형을 만드는 것이라고 가정합니다.
XXX 대한 변수를 지정하고 반환되는 실제 값을 확인합니다.
모두 XXX 로 통합할 수 있습니다. 그 유형 변수는 이론적으로 다음과 같아야 합니다.
우리의 대답을 알려주세요. 하지만 물론 문제는 이 유형이
변수는 범위에 없는 많은 지역을 참조할 수 있습니다.
fn 서명 -- 예를 들어, fn 본문의 영역. (이 같은 문제
유형에서는 발생하지 않습니다. 기술적으로
예를 들어 fn 본문의 구조체 선언은 이름을 지정할 수 없습니다.
그것은 일종의 인위적인 제한입니다.
fn 외부의 구조체)

struct desugaring 또는 impl 둘 다를 보면
(Rust의 어휘 구조에 내재되어 있음) XXX 할 수 있는 제한
'static 또는 'a'b 와 같은 수명 중 하나만 이름을 지정하십시오.
함수 서명에 나타납니다. 그것이 우리가 아닌 것입니다.
여기 모델링. 가장 좋은 방법은 잘 모르겠습니다 -- 어떤 유형
추론 체계는 범위 지정을 보다 직접적으로 표현하고,
저는 클로저에 도움을 주기 위해 항상 이것을 Rust에 추가하고 싶었습니다. 하지만
먼저 더 작은 델타에 대해 생각해 보겠습니다.

여기서 \in 제약 조건이 발생합니다. 추가하는 것을 상상할 수 있습니다.
(기본적으로) FR(XXX) \subset {'a, 'b} --
즉, XXX에 나타나는 "자유 지역"은 'a 있으며
'b . 이것은 \in 요구 사항으로 변환됩니다.
XXX 나타나는 다양한 지역

실제 예를 살펴보겠습니다.

fn foo<'a,'b>(x: &'a [u32], y: &'b [u32]) -> impl Iterator<Item=u32> {
    if condition { x.iter().cloned() } else { y.iter().cloned() }
}

여기서 condition 가 true인 경우 유형은 다음과 같습니다.
Cloned<SliceIter<'a, i32>> . 그러나 condition 가 거짓이면 우리는
Cloned<SliceIter<'b, i32>> 원합니다. 물론 두 경우 모두 우리는
(유형/지역 변수에 숫자 사용)과 같은 결과를 가져옵니다.

Cloned<SliceIter<'0, i32>> <: 0
'a: '0 // because the source is x.iter()
Cloned<SliceIter<'1, i32>> <: 0
'b: '1 // because the source is y.iter()

그런 다음 변수 0을 Cloned<SliceIter<'2, i32>> 로 인스턴스화하면
'0: '2'1: '2 또는 전체 지역 관계 세트가 있습니다.
처럼:

'a: '0
'0: '2
'b: '1
'1: '2
'2: 'body // the lifetime of the fn body

그렇다면 '2 어떤 값을 사용해야 합니까? 우리는 또한 추가
'2 in {'a, 'b} 라는 제약 조건. fn이 쓰여진 상태에서 우리는
'a'b 도 a가 아니므로 오류를 보고해야 합니다.
올바른 선택. 그러나 흥미롭게도 'a: 'b 제약 조건을 추가하면 올바른 값( 'b )이 됩니다.

_normal_ 알고리즘을 실행하면
'2'body 입니다. \in 관계를 처리하는 방법을 잘 모르겠습니다.
철저한 검색을 제외하고(나는 몇 가지 특별한 것을 상상할 수 있지만
경우).

좋아, 여기까지야. =)

PR #35091에서 @arielb1 은 다음과 같이 썼습니다.

나는 "impl 특성에서 모든 수명 캡처" 접근 방식을 좋아하지 않으며 평생 제거와 같은 것을 선호합니다.

여기에서 토론하는 것이 더 합리적이라고 생각했습니다. @arielb1 , 염두에 두고 있는 것에 대해 더 자세히 <() as FooReturn<'a,'b>>::Type 대신 <() as FooReturn<'a>>::Type <() as FooReturn<'a,'b>>::Type 또는 무엇인가?

나는 존재하는 수명 생략 규칙이 이 점에서 좋은 지침이 될 것이라고 생각하지 않습니다. &self 의 수명만 포함하도록 선택했다면 반드시 포함할 수는 없을 것입니다. Self 구조체의 유형 매개변수나 메서드의 유형 매개변수가 아닙니다. 다른 수명의 이름을 지정해야 하는 WF 조건이 있을 수 있기 때문입니다.

어쨌든, 염두에 두고 있는 규칙과 그 이점을 설명하는 몇 가지 예를 보는 것이 좋습니다. :) (또한 선택을 재정의하려면 몇 가지 구문이 필요하다고 생각합니다.) 다른 모든 조건이 동일하다면 N개의 수명 중에서 선택해야 하는 것을 피할 수 있다면 그것을 선호합니다.

impl Trait 개인 정보 보호 관련 상호 작용을 본 적이 없습니다.
이제 fn f() -> impl Traitfn f() -> Box<Trait> 특성 개체와 유사하게 개인 유형 S: Trait 반환할 수 있습니다. 즉, private 유형의 객체는 익명화된 형태로 모듈 외부에서 자유롭게 이동할 수 있습니다.
이것은 합리적이고 바람직한 것 같습니다. 유형 자체는 구현 세부사항이며 public 특성 Trait 통해 사용할 수 있는 인터페이스만 공용입니다.
그러나 특성 개체와 impl Trait 사이에는 한 가지 차이점이 있습니다. 트레이트 개체만 사용하면 개인 유형의 모든 트레이트 메서드가 내부 연결을 얻을 수 있으며 여전히 함수 포인터를 통해 호출할 수 있습니다. impl Trait 사용하면 private 유형의 특성 메소드를 다른 번역 단위에서 직접 호출할 수 있습니다. 기호의 "내부화"를 수행하는 알고리즘은 impl Trait 익명화되지 않은 유형에 대해서만 메소드를 내부화하거나 매우 비관적이어야 합니다.

@nikomatsakis

foo 를 쓰는 "명시적" 방법은 다음과 같습니다.

fn foo<'a: 'c,'b: 'c,'c>(x: &'a [u32], y: &'b [u32]) -> impl Iterator<Item=u32> + 'c {
    if condition { x.iter().cloned() } else { y.iter().cloned() }
}

여기서 평생 경계에 대해 의문의 여지가 없습니다. 분명히, 매번 수명 경계를 작성해야 하는 것은 꽤 반복적일 것입니다. 그러나 우리가 그런 종류의 반복을 처리하는 방법은 일반적으로 평생 생략을 통해 이루어집니다. foo 경우 생략이 실패하고 프로그래머가 명시적으로 수명을 지정해야 합니다.

나는 @eddybimpl Trait 의 특정한 경우에만 했던 것처럼 명시 성에 민감한 평생

@arielb1 흠, 제가 논의한 "디슈가링"의 관점에서 이 제안된 구문을 어떻게 생각해야 하는지 100% 확신할 수 없습니다. 수명 제한으로 나타나는 것을 지정할 수 있지만 우리가 추론하려는 것은 대부분 숨겨진 유형에 표시되는 수명입니다. 이것은 최대 하나의 수명이 "숨겨질" 수 있음을 시사합니까(그리고 정확히 지정해야 합니까?)

"단일 수명 매개변수"로 충분하지 않은 경우가 항상 있는 것 같습니다.

fn foo<'a, 'b>(x: &'a [u32], y: &'b [u32]) -> impl Iterator<Item=u32> {
    x.iter().chain(y).cloned()
}

이 경우 숨겨진 반복기 유형은 'a'b 를 모두 참조합니다(둘 다 변형이지만 불변인 예를 생각해낼 수 있을 것 같습니다).

그래서 @aturon 과 저는 이 문제에 대해 다소 논의했고 공유하고 싶었습니다. 여기에는 실제로 몇 가지 직교 질문이 있으며 나는 그것들을 분리하고 싶습니다. 첫 번째 질문은 "숨겨진 유형에서 잠재적으로 사용할 수 있는 유형/수명 매개변수는 무엇입니까?"입니다. default type 로의 (준)디슈가링의 관점에서 이것은 "우리가 도입한 특성에 나타나는 유형 매개변수"로 귀결됩니다. 예를 들어 이 기능이 다음과 같은 경우:

fn foo<'a, 'b, T>() -> impl Trait { ... }

다음과 같이 완화됩니다.

fn foo<'a, 'b, T>() -> <() as Foo<...>>::Type { ... }
trait Foo<...> {
  type Type: Trait;
}
impl<...> Foo<...> for () {
  default type Type = /* inferred */;
}

그러면 이 질문은 " Foo 특성과 해당 impl에 나타나는 유형 매개변수"로 귀결됩니다. 기본적으로 여기 ... 입니다. 분명히 여기에는 Trait 자체에서 사용되는 유형 매개변수 세트가 포함되지만 추가 유형 매개변수는 무엇입니까? (이전에 언급했듯이 이 디슈가링은 auto 특성의 누출을 제외하고는 100% 충실하며, 전문화 가능한 impls에 대해서도 auto 특성을 누출해야 한다고 주장합니다.)

우리가 사용한 기본 대답은 "모두"이므로 여기서 ...'a, 'b, T 가 됩니다(나타날 수 있는 모든 익명 매개변수와 함께). 이것은 합리적인 기본값일 수도 있지만 _필연적으로_ 최상의 기본값은 아닙니다. ( @arielb1이 지적했듯이.)

이것은 <() as Foo<...>>::Type ( impl Trait 의 특정 불투명 인스턴스화 참조)가 'x 보다 오래 산다는 것을 결정하기 위해 효과적으로 다음을 표시해야 하므로 수명 관계에 영향을 미칩니다. ...: 'x (즉, 모든 수명 및 유형 매개변수).

이것이 내가 평생 매개변수를 고려하는 것만으로는 충분하지 않다고 말하는 이유입니다. foo 와 같은 foo::<'a0, 'b0, &'c0 i32> foo 대한 호출이 있다고 상상해 보십시오. 이것은 '[abc]0 세 가지 수명이 모두 'x 보다 오래 지속되어야 함을 의미합니다. . 그러나 @arielb1이 지적했듯이 elision은 이것이 일반적으로 필요 이상으로 길어질 것이라고 제안합니다.

그래서 저는 우리에게 필요한 것이 다음과 같다고 생각합니다.

  • 아마도 생략의 직관을 사용하여 합당한 채무 불이행을 해결하기 위해;
  • 기본값이 적절하지 않은 경우에 대한 명시적 구문을 사용합니다.

@aturonimpl<...> Trait 와 같은 것을 명시적 구문으로 뱉어 냈습니다 . 이는 합리적으로 보입니다. 따라서 다음과 같이 작성할 수 있습니다.

fn foo<'a, 'b, T>(...) -> impl<T> Trait { }

숨겨진 유형이 실제로하지 않는 것을 나타냅니다 참조 'a 또는 'bT . 또는 'b 또는 T 가 캡처되지 않았음을 나타내기 위해 impl<'a> Trait 를 작성할 수 있습니다.

기본값에 관해서는 더 많은 데이터가 있는 것이 꽤 유용할 것 같지만 제거의 일반적인 논리는 적용 가능한 경우 self 유형으로 명명된 모든 매개변수를 캡처하는 것이 좋습니다. 예를 들어 fn foo<'a,'b>(&'a self, v: &'b [u8]) 있고 유형이 Bar<'c, X> 이면 self 유형은 &'a Bar<'c, X> 이므로 'a 캡처합니다 'cX 가 기본적으로 있지만 'b 아닙니다.


또 다른 관련 참고 사항은 평생 제한의 의미입니다. 소리 수명 범위는 변경되어서는 안 되는 기존 의미가 있다고 생각합니다. impl (Trait+'a) 라고 쓰면 숨겨진 유형 T 'a 보다 오래 산다는 의미입니다. 유사하게 impl (Trait+'static) 를 작성하여 빌린 포인터가 없음을 나타낼 수 있습니다(일부 수명이 캡처된 경우에도). 숨겨진 유형 T 추론할 때 이것은 $T: 'static 와 같은 수명 제한을 의미합니다. 여기서 $T 는 숨겨진 유형에 대해 생성하는 추론 변수입니다. 이것은 일반적인 방식으로 처리됩니다. 숨겨진 유형이 숨겨진 호출자의 관점에서 볼 때 'static 경계는 캡처된 수명 매개변수가 있더라도 impl (Trait+'static) 'static 보다 오래 산다는 결론을 내릴 수 있습니다.

여기에서는 디슈가링이 작동하는 것과 정확히 동일하게 작동합니다.

fn foo<'a, 'b, T>() -> <() as Foo<'a, 'b, 'T>>::Type { ... }
trait Foo<'a, 'b, T> {
  type Type: Trait + 'static; // <-- note the `'static` bound appears here
}
impl<'a, 'b, T> Foo<...> for () {
  default type Type = /* something that doesn't reference `'a`, `'b`, or `T` */;
}

이 모든 것은 추론에서 직교합니다. 우리는 여전히 (제 생각에) "선택" 제약 조건의 개념을 추가하고 일부 경험적 방법으로 추론을 수정하고 가능하면 철저한 검색을 원합니다(RFC 1214의 경험에 따르면 보수적 폴백을 사용한 경험적 방법이 실제로 우리를 매우 멀리 얻을 수 있음을 시사합니다. 어딘가에 문제가 있을 수 있지만 이 점에서 사람들이 한계에 부딪힌다는 사실을 알지 못합니다.) 확실히 'static 또는 '``와 같은 수명 범위를 추가하면 추론에 영향을 미칠 수 있으므로 도움이 될 수 있지만 이것이 완벽한 솔루션은 아닙니다. 우선 호출자에게 표시되고 API의 일부가 됩니다. 원하지 않을 수 있습니다.

가능한 옵션:

출력 매개변수 생략과 함께 명시적 수명 제한

오늘날의 특성 개체와 마찬가지로 impl Trait 개체에는 제거 규칙을 사용하여 추론되는 단일 수명 제한 매개변수가 있습니다.

단점: 인체공학적이지 않음
이점: 명확한

"모든 일반" 생략이 있는 명시적 수명 범위

오늘날의 특성 개체와 마찬가지로 impl Trait 개체에는 단일 수명 제한 매개변수가 있습니다.

그러나 elision은 모든 명시적 매개변수보다 오래 지속되는 새로운 초기 바인딩 매개변수를 생성합니다.

fn foo<T>(&T) -> impl Foo
-->
fn foo<'total, T: 'total>(&T) -> impl Foo + 'total

단점: 조기 바인딩된 매개변수를 추가합니다.

더.

나는 impl Trait +'a 및 차용으로 이 문제에 부딪쳤습니다: https://github.com/rust-lang/rust/issues/37790

이 변경 사항을 올바르게 이해하고 있다면(그리고 그럴 가능성은 낮을 것입니다!), 이 플레이그라운드 코드가 작동해야 한다고 생각합니다.

https://play.rust-lang.org/?gist=496ec05e6fa9d3a761df09c95297aa2a&version=nightly&backtrace=0

ThingOneThingTwo 모두 Thing 특성을 구현합니다. buildThing 를 구현하는 것을 반환할 것이라고 말합니다. 그러나 컴파일되지 않습니다. 그래서 나는 분명히 뭔가를 오해하고 있습니다.

그 "무언가"에는 유형이 있어야 하지만 귀하의 경우에는 두 가지 충돌하는 유형이 있습니다. @nikomatsakis 는 이전에 유형 불일치가 나타날 때 ThingOne | ThingTwo 를 생성하여 일반적으로 이 작업을 수행할 것을 제안했습니다.

@eddyb ThingOne | ThingTwo 에 대해 자세히 설명해 주 시겠습니까? 런타임에 유형만 알고 있다면 Box 가 필요하지 않습니까? 아니면 일종의 enum 입니까?

예, 가능한 경우 특성 메서드 호출을 해당 변종에 위임한 임시 enum 유형일 수 있습니다.

나도 예전에 그런 걸 원했어. 익명의 열거형 RFC: https://github.com/rust-lang/rfcs/pull/1154

추론 기반인 경우 더 잘 작동하는 드문 경우입니다. 불일치에 대해서만 이러한 유형을 생성 하면 변형이 다르기 때문입니다 (이는 일반화된 형식의 문제임).
또한 패턴 일치가 없는 것으로부터 무언가를 얻을 수 있습니다(분명히 분리된 경우 제외?).
그러나 IMO 위임 설탕은 T | T 를) 얻더라도 모든 관련 사례에서 "그냥 작동"합니다.

그 문장의 다른 암묵적인 절반을 철자할 수 있습니까? 나는 그것의 대부분을 이해하지 못하며 약간의 맥락을 놓치고 있다고 생각합니다. 유니온 유형의 문제에 암묵적으로 대응했습니까? RFC는 유니온 유형이 아닌 단순히 익명의 열거형입니다. (T|T)Result<T, T> 만큼 문제가 됩니다.

아, 신경쓰지 마세요. 제안이 혼란스러워졌습니다(또한 고장난 HDD를 분류할 때까지 모바일에서 멈췄습니다. 그래서 트위터처럼 들리는 것에 대해 사과드립니다).

(위치, 즉 T|U != U|T ) 익명 열거형이 흥미롭고 가변 제네릭이 있는 경우 라이브러리에서 실험할 수 있다고 생각합니다( hlist 를 사용하여 이를 우회할 수 있음). const 제네릭(피아노 번호와 동일).

그러나 동시에 언어 지원이 있다면 익명 열거형이 아니라 공용체 유형이 될 것입니다. 예를 들어 Result 아니라 오류 유형입니다(이름이 지정된 래퍼의 지루함을 우회하기 위해).

여기가 적절한 질문인지 모르겠지만 impl 와 같은 키워드가 필요한 이유는 무엇입니까? 토론을 찾을 수 없습니다(내 잘못일 수 있음).

함수가 impl Trait를 반환하는 경우 해당 본문은 Trait를 구현하는 모든 유형의 값을 반환할 수 있습니다.

부터

fn bar(a: &Foo) {
  ...
}

" Foo 특성을 구현하는 유형에 대한 참조 허용"을 의미합니다.

fn bar() -> Foo {
  ...
}

" Foo 특성을 구현하는 유형을 반환"을 의미합니다. 불가능한가요?

@kud1ing 하는 이유는 향후 동적으로 크기가 조정된 반환 값에 대한 지원이 추가되는 경우 동적으로 크기가 조정된 Trait 유형을 반환하는 함수를 가질 가능성을 제거하지 않기 위함입니다. 현재 Trait 은 이미 유효한 DST이며 DST를 반환할 수 없으므로 크기가 지정된 유형으로 만들기 위해 상자에 넣어야 합니다.

편집: 연결된 RFC 스레드에서 이에 대한 논의가 있습니다.

글쎄, 우선, 동적으로 크기가 조정된 반환 값이 추가되는지 여부에 관계없이 현재 구문을 선호합니다. 특성 개체에서 발생하는 것과 달리 이것은 유형 삭제가 아니며 "매개변수 f: &FooFoo impls를 사용하는 반면 이것은 Foo impls를 반환하는 것"과 같은 우연의 일치는 오해의 소지가 있을 수 있습니다.

나는 RFC 토론에서 지금 impl 이 자리 표시자 구현이고 impl 가 그다지 바람직하지 않다는 것을 모았습니다. 반환 값이 DST가 아닌 경우 impl 특성을 원하지 _not_ 이유가 있습니까?

"자동 특성 누출"을 처리하는 현재 impl 기술이 문제가 있다고 생각합니다. 대신 fn fn foo() -> impl Iterator 를 정의하고 호출자 fn bar() { ... foo() ... } 가 있는 경우 bar() 전에 foo() 유형을 검사해야 하도록 DAG 순서를 적용해야 합니다 fn bar() { ... foo() ... } bar() (숨겨진 유형이 무엇인지 알 수 있도록). 주기가 발생하면 오류가 보고됩니다. 이것은 보수적인 입장입니다. 우리가 더 잘할 수 있습니다. 그러나 자동 특성 의무를 수집하고 마지막에 확인하는 현재 기술은 일반적으로 작동하지 않는다고 생각합니다. 예를 들어, 전문화에서는 잘 작동하지 않습니다.

(엄격한 DAG를 요구하는 것보다 더 관대할 수 있는 또 다른 가능성은 두 fns를 어느 정도 "함께" 유형 검사하는 것입니다. 이는 특성 시스템 impl을 약간 재구성한 후에만 고려해야 할 사항이라고 생각합니다.)

@Nercury 이해가 안됩니다. fn foo() -> Trait-> impl Trait 를 의미하지 않는 이유가 있는지 묻는 것입니까?

@nikomatsakis 네, 정확히 물어본 것입니다. 복잡한 언어를 사용해서 죄송합니다. :). impl 키워드 없이 이 작업을 수행하는 것이 더 간단할 것이라고 생각했습니다. 왜냐하면 이 동작이 정확히 예상되는 것과 같기 때문입니다(특성 반환 유형 대신 구체적인 유형이 반환되는 경우). 다만, 제가 놓친 부분이 있어서 여쭤봅니다.

차이점은 impl Trait 반환하는 함수는 항상 같은 유형을 반환한다는 것입니다. 기본적으로 반환 유형 유추입니다. IIUC에서 Trait 만 반환하는 함수는 해당 특성의 구현을 동적으로 반환할 수 있지만 호출자는 box foo() 와 같은 것을 통해 반환 값을 위한 공간을 할당할 준비가 되어 있어야 합니다.

@Nercury 간단한 이유는 -> Trait 구문에 이미 의미가 있으므로 이 기능에 대해 다른 것을 사용해야 합니다.

나는 실제로 사람들이 기본적으로 두 가지 종류의 행동을 모두 기대하는 것을 보았고 이러한 종류의 혼란이 자주 발생합니다. 솔직히 말해서 fn foo() -> Trait 가 아무 의미가 없고(또는 기본적으로 경고가 됨) 명시적 "선택할 수 있지만 호출자가 볼 수 없는 컴파일 시간에 알려진 일부 유형" 케이스와 "특성을 구현하는 모든 유형에 동적으로 디스패치할 수 있는 특성 개체" 케이스 모두에 대한 키워드(예: fn foo() -> impl Traitfn foo() -> dyn Trait . 그러나 분명히 그 배들은 항해했습니다.

컴파일러가 함수의 다른 모든 반환 유형을 보유하는 열거형을 생성하지 않고 각 변형에 대한 인수를 통해 전달하는 특성을 구현하고 대신 반환하는 이유는 무엇입니까?

그것은 하나의 반환 유형 허용 규칙을 우회합니다.

@NeoLegends 이 작업을 수동으로 수행하는 것은 상당히 일반적이며 이에 대한 일부 설탕은 훌륭할 수 있고 과거에 제안되었지만 impl Trait 또는 특성 개체를 반환하는 것과 완전히 다른 세 번째 의미 체계 집합이므로 그렇지 않습니다. 이 토론과 정말 관련이 있습니다.

@Ixrec 예, 이것이 수동으로 수행되고 있다는 것을 알고 있지만 컴파일러에서 생성된 반환 유형으로 익명 열거형의 실제 사용 사례는 긴 반복기 또는 미래 어댑터와 같이 철자법을 알 수 없는 유형입니다.

이것은 어떻게 다른 의미입니까? 익명 열거형(컴파일러가 생성하는 한 익명 열거형 RFC에 따라가 아님)은 다른 변형을 추상화하는 특성과 같은 공통 API가 있는 경우에만 실제로 의미가 있습니다. API의 소비자가 직접 볼 수 없는 컴파일러 생성 열거형을 통해 단일 유형 제한이 제거된 일반 impl Trait처럼 보이고 작동하는 기능을 제안하고 있습니다. 소비자는 항상 'impl Trait'만 볼 수 있습니다.

익명의 자동 생성 열거형은 놓치기 쉬운 impl Trait 숨겨진 비용을 제공하므로 고려해야 할 사항입니다.

"auto enum pass-through"는 객체 안전 특성에 대해서만 의미가 있다고 생각합니다. impl Trait 자체도 마찬가지인가요?

@rpjohnst 이것이 아니라면 실제 메소드 변형은 크레이트 메타데이터에 있고 호출 사이트에서

@glaebhoerl

"auto enum pass-through"는 객체 안전 특성에 대해서만 의미가 있다고 생각합니다. impl Trait 자체도 마찬가지인가요?

이것은 흥미로운 포인트입니다! 나는 impl 특성을 "디슈거(desugar)"하는 올바른 방법이 무엇인지 에 대해 토론유형 패밀리와 결합할 때 Haskell에서 건전하지 않은 것으로 유명하게 밝혀졌습니다. "캐시에서" 이 불건전함을 완전히 이해하지 못했다는 것을 고백하지만 impl에서 F<T> 유형에 대한 특성 구현을 자동으로 생성하려면 여기에서 매우 조심해야 할 것 같습니다. T .

@nikomatsakis

문제는 Rust 용어로

trait Foo {
    type Output;
    fn get() -> Self::Output;
}

fn foo() -> impl Foo {
    // ...
    // what is the type of return_type::get?
}

tl;dr은 일반화된 newtype 파생이 vtable을 transmute ing함으로써 구현되었다는 것입니다. 결국 vtable은 해당 유형에 대한 함수로 구성되며 유형과 해당 newtype은 동일한 표현을 갖습니다. , 그럼 괜찮아, 그렇지? 그러나 해당 함수가 주어진 유형의 ID (표현이 아닌)에 대한 유형 수준 분기에 의해 결정된 유형을 사용하는 경우에도 중단됩니다. 예를 들어 유형 함수 또는 관련 유형(또는 Haskell에서는 GADT)을 사용합니다. 이러한 유형의 표현도 호환된다는 보장이 없기 때문입니다.

이 문제는 안전하지 않은 변환을 사용하는 경우에만 가능합니다. 대신 어디에서나 newtype을 래핑/언래핑하는 지루한 상용구 코드를 생성하고 모든 메소드를 기본 유형에서 구현으로 전달하는 경우(예: Rust IIRC에 대한 자동 위임 제안), 최악의 결과는 유형이 될 것입니다. 오류 또는 아마도 ICE. 결국, 구성상 안전하지 않은 코드를 사용하지 않으면 안전하지 않은 결과를 얻을 수 없습니다. 마찬가지로 "자동 열거형 통과"에 대한 코드를 생성했지만 이를 위해 unsafe 프리미티브를 사용하지 않았다면 위험이 없었을 것입니다.

(하지만 이것이 impl Trait 및/또는 자동 열거형 통과와 함께 사용되는 특성이 필요에 따라 객체로부터 안전해야 하는지 여부에 대한 원래 질문과 관련이 있는지 여부는 확실하지 않습니다.)

@rpjohnst One은 비용을 표시하기 위해 enum 케이스를 선택하도록 할 수 있습니다.

fn foo() -> enum impl Trait { ... }

그것은 거의 확실히 다른 RFC를 위한 음식입니다.

@glaebhoerl 예, 나는 문제를 파헤치는 데 시간을 보냈고 적어도 여기에서는 문제가 되지 않을 것이라고 상당히 확신했습니다.

명백한 것이라면 죄송하지만 impl Trait 가 특성 메소드의 반환 유형에 나타날 수 없는 이유 또는 처음부터 의미가 있는지 이해하려고 노력하고 있습니다. 예:

trait IterInto {
    type Output;
    fn iter_into(&self) -> impl Iterator<Item=impl Into<Self::Output>>;
}

@aldanor 그것은 완전히 의미가 있으며 AFAIK의 의도는 그 작업을 수행하는 것이지만 아직 구현되지 않았습니다.

그것은 일종의 의미가 있지만 동일한 기본 기능이 아닙니다(이는 btw에서 많이 논의되었습니다).

// What that trait would desugar into:
trait IterInto {
    type Output;
    type X: Into<Self::Output>;
    type Y: Iterator<Item=Self::X>;
    fn iter_into(&self) -> Self::Y;
}

// What an implementation would desugar into:
impl InterInto for FooList {
    type Output = Foo;
    // These could potentially be left unspecified for
    // a similar effect, if we want to allow that.
    type X = impl Into<Foo>;
    type Y = impl Iterator<Item=Self::X>;
    fn iter_into(&self) -> Self::Y {...}
}

특히, impl Traitimpl Trait for Type 관련된 유형 'RHSes, 그것은 안정 녹에 desugared되지 않을 수 있다는 점에서, 현재 구현 된 기능과 유사 것에 반면 trait 그것은 될 수 있습니다.

나는 이것이 아마도 너무 늦고 대부분은 자전거 이동이라는 것을 알고 있지만 impl 키워드가 도입된 이유가 어디에나 문서화되어 있습니까? 현재 Rust 코드에 "컴파일러가 여기에 어떤 유형이 들어가는지 파악합니다", 즉 _ 라고 말하는 방법이 이미 있는 것 같습니다. 구문을 제공하기 위해 여기에서 이것을 재사용할 수 없습니까?

fn foo() -> _ as Iterator<Item=u8> {}

@jonhoo 그것은 기능이 하는 것이 아니라 유형이 함수에서 반환된 것이 아니라 선택한 API(및 OIBIT가 고통스럽기 때문에)를 제외한 모든 것을 숨기는 "의미론적 래퍼"입니다.

우리는 일부 함수가 DAG를 강제하여 서명에서 유형을 추론하도록 허용할 수 있지만 그러한 기능은 승인된 적이 없으며 "전역 추론"을 다루기 때문에 Rust에 추가될 가능성이 거의 없습니다.

여기에 언급된 대로 @Trait 구문을 사용하여 impl Trait 를 대체할 것을 제안합니다.

Box<@MyTrait> 또는 &@MyTrait 와 같은 다른 유형 위치 및 구성으로 확장하는 것이 더 쉽습니다.

@Trait 에 대해 any T where T: Trait~Trait 에 대해 some T where T: Trait :

fn compose<T, U, V>(f: @Fn(T) -> U, g: @Fn(U) -> V) -> ~Fn(T) -> V {
    move |x| g(f(x))
}

fn func(t: T) -> V 에서는 특성으로 t 또는 v를 구별할 필요가 없습니다.

fn compose<T, U, V>(f: @Fn(T) -> U, g: @Fn(U) -> V) -> @Fn(T) -> V {
    move |x| g(f(x))
}

여전히 작동합니다.

@JF-Liu 저는 개인적으로 anysome 를 하나의 키워드/시질로 통합하는 것에 반대합니다. 그러나 우리가 하나의 시질을 갖고 원래 impl Trait 처럼 사용할 수 있다는 것은 기술적으로 옳습니다.

@JF-Liu @eddyb 언어에서 인장이 제거된 이유가 있습니다. 그 이유가 이 경우에 적용되지 않는 이유는 무엇입니까?

@ 는 패턴 일치에도 사용되며 언어에서 제거되지 않습니다.

내가 염두에 둔 것은 AFAIK sigils가 과도하게 사용되었다는 것입니다.

구문 bikesheding: impl Trait 표기법에 대해 매우 불만입니다. 키워드(편집기에서 굵은 글꼴)를 사용하여 유형 이름을 지정하는 것이 너무 시끄럽기 때문입니다. C의 struct 및 Stroustroup 시끄러운 구문 관찰 (슬라이드 14)을 기억하십니까?

https://internals.rust-lang.org/t/ideas-for-making-rust-easier-for-beginners/4761 에서 @konstin<Trait> 구문을 제안했습니다. 특히 입력 위치에서 정말 멋지게 보입니다.

fn take_iterator(iterator: <Iterator<Item=i32>>)

UFC와 다소 충돌할 것으로 보지만 이것이 해결될 수 있을까요?

나도 impl Trait 대신 꺾쇠 괄호를 사용하여 적어도 반환 유형 위치에서 더 나은 선택이라고 생각합니다. 예:

fn returns_iter() -> <Iterator<Item=i32>> {...}
fn returns_closure() -> <FnOnce() -> bool> {...}

<Trait> 구문이 제네릭과 충돌하는 경우 다음을 고려하십시오.

Vec<<FnOnce() -> bool>>Vec<@FnOnce() -> bool>

Vec<FnOnce() -> bool> 가 허용되면 <Trait> 가 좋은 생각이며 제네릭 유형 매개변수와 동등함을 나타냅니다. 그러나 Box<Trait>Box<@Trait> 와 다르기 때문에 <Trait> 구문을 포기해야 합니다.

나는 impl 키워드 구문을 선호합니다. 왜냐하면 문서를 빠르게 읽을 때 프로토타입을 잘못 읽을 수 있는 방법이 줄어들기 때문입니다.
어떻게 생각해 ?

내부 스레드 에서 이 rfc에 대한 상위 집합을 제안 했음을 깨달았 습니다(여기를

다음 예제와 같이 꺾쇠 괄호로 둘러싸서 함수 매개변수 및 반환 유형의 특성을 사용할 수 있습니다.

fn transform(iter: <Iterator>) -> <Iterator> {
    // ...
}

그런 다음 컴파일러는 현재 제네릭에 적용된 것과 동일한 규칙을 사용하여 매개변수를 단일화합니다. 반환 유형은 예를 들어 함수 구현에서 파생될 수 있습니다. 이것은 단순히 Box<Trait_with_transform> 에서 이 메서드를 호출하거나 일반적으로 동적으로 디스패치된 개체에서 사용할 수 없지만 여전히 규칙을 더 관대하게 만듭니다. 나는 모든 RFC 토론을 읽지 않았으므로 내가 놓친 더 나은 솔루션이 이미 있을 수 있습니다.

나는 문서를 빠르게 읽을 때 프로토타입을 잘못 읽을 가능성이 적기 때문에 impl 키워드 구문을 선호합니다.

구문 강조 표시의 다른 색상이 트릭을 수행해야 합니다.

Stroustrup의 이 백서는 섹션 7에서 C++ 개념에 대한 유사한 구문 선택에 대해 설명합니다. http://www.stroustrup.com/good_concepts.pdf

제네릭과 실존에 대해 동일한 구문을 사용하지 마십시오. 그것들은 같은 것이 아닙니다. 제네릭을 사용하면 호출자가 구체적인 유형이 무엇인지 결정할 수 있지만 (이 제한된 하위 집합) 실존 항목을 사용하면 호출되는 함수가 구체적 유형이 무엇인지 결정할 수 있습니다. 이 예:

fn transform(iter: <Iterator>) -> <Iterator>

이것과 동등해야합니다

fn transform<T: Iterator, U: Iterator>(iter: T) -> U

또는 이것과 동등해야합니다

fn transform(iter: impl Iterator) -> impl Iterator

마지막 예제는 야간에도 올바르게 컴파일되지 않고 iterator 특성으로 실제로 호출할 수 없지만 FromIter 와 같은 특성을 사용하면 호출자가 인스턴스를 생성하고 이를 수행하지 않고도 함수에 전달할 수 있습니다. 전달하는 항목의 구체적인 유형을 결정합니다.

구문은 유사해야 하지만 동일해서는 안 됩니다.

유형 이름에서 (generics) 또는 일부 (existentials)를 구분할 필요가 없으며 유형이 사용되는 위치에 따라 다릅니다. 변수, 인수 및 구조체 필드에서 사용할 때 항상 T 중 하나를 허용하고 fn 반환 유형에서 사용할 때 항상 일부 T를 가져옵니다.

  • 구체적인 데이터 유형에 Type , &Type , Box<Type> , 정적 디스패치
  • 추상 데이터 유형, 정적 디스패치에 대해 @Trait , &@Trait , Box<@Trait> 및 일반 유형 매개변수 사용
  • 추상 데이터 유형, 동적 디스패치에 &Trait , Box<Trait>

fn func(x: @Trait)fn func<T: Trait>(x: T) .
fn func<T1: Trait, T2: Trait>(x: T1, y: T2) 는 간단히 fn func(x: <strong i="22">@Trait</strong>, y: @Trait) 로 쓸 수 있습니다.
T 매개변수는 fn func<T: Trait>(x: T, y: T) 여전히 필요합니다.

struct Foo { field: <strong i="28">@Trait</strong> }struct Foo<T: Trait> { field: T } .

변수, 인수 및 구조체 필드에서 사용할 때 항상 T 중 하나를 허용하고 fn 반환 유형에서 사용할 때 항상 일부 T를 가져옵니다.

기존의 제네릭 구문을 사용하여 안정적인 Rust에서 지금 당장 any-of-Trait를 반환할 수 있습니다. 많이 사용하는 기능입니다. serde_json::de::from_slice&[u8] 를 매개변수로 사용하고 T where T: Deserialize 반환합니다.

또한 의미 있는 일부 특성을 반환할 수 있으며 이것이 바로 우리가 논의 중인 기능입니다. 제네릭을 사용하여 박싱되지 않은 클로저를 반환할 수 없는 것처럼 역직렬화 함수에 대해 실존을 사용할 수 없습니다. 그것들은 다른 기능입니다.

더 친숙한 예를 들어 Iterator::collectT where T: FromIterator<Self::Item> 반환할 수 있으며 이는 내가 선호하는 표기법인 fn collect(self) -> any FromIterator<Self::Item> 합니다.

구문은 어떻습니까
fn foo () -> _ : Trait { ... }
반환 값 및
fn foo (m: _1, n: _2) -> _ : Trait where _1: Trait1, _2: Trait2 { ... }
매개변수?

나에게 새로운 제안 중 어느 것도 우아함에서 impl Trait 에 가깝지 않습니다. impl 는 이미 모든 Rust 프로그래머에게 알려진 키워드이며 특성을 구현하는 데 사용되기 때문에 실제로 기능이 자체적으로 수행하는 작업을 제안합니다.

예, 기존 키워드를 고수하는 것이 이상적입니다. 나는 실존주의를 위한 impl 및 보편주의를 위한 for 를 보고 싶습니다.

저는 개인적으로 anysome 를 하나의 키워드/시질로 통합하는 것을 반대합니다.

@eddyb 나는 그것을 융합이라고 생각하지 않을 것입니다. 다음 규칙에서 자연스럽게 따릅니다.

((∃ T . F⟨T⟩) → R)  →  ∀ T . (F⟨T⟩ → R)

편집: 동형이 아니라 단방향입니다.


관련 없음: 다음과 같은 다른 공변 위치에 impl Trait 도 허용하는 관련 제안이 있습니까?

~녹fn 푸(콜백: F) -> R여기서 F: FnOnce(impl SomeTrait) -> R {콜백(create_something())}~

지금 당장impl SomeTrait 구체적인 시간을 할애할 수 있기 때문에 이것은 필수 기능이 아닙니다. 가독성은 떨어지지만 그렇지 않으면 큰 문제가 되지 않습니다.

그러나 RFC 1522 기능이 안정화되면 create_something 결과가 impl SomeTrait 되면 위와 같은 프로그램에 유형 서명을 할당하는 것이

@Rufflewind 현실 세계에서는 상황이 그렇게 명확하지 않으며 이 기능은 매우 특정한 브랜드의 존재입니다(Rust에는 현재 몇 가지가 있습니다).

그러나 그때조차도 공분산을 사용하여 impl Trait 함수 인수 내부 및 외부에서 무엇을 의미하는지 결정하기

그것으로는 충분하지 않습니다:

  • 기본값의 반대를 사용하여
  • 필드 유형 내에서 명확화( anysome 모두 동일하게 바람직함)

@Rufflewind impl Trait 가 무엇인지에 대한 잘못된 괄호처럼 보입니다. 나는 Haskell이 이 관계를 이용하여 forall 키워드만 사용하여 보편성과 실존성을 모두 표현한다는 것을 알고 있지만, 우리가 논의하는 맥락에서는 작동하지 않습니다.

예를 들어 다음과 같이 정의하십시오.

fn foo(x: impl ArgTrait) -> impl ReturnTrait { ... }

" 인수의 impl 는 보편적이고 반환 유형의 impl 는 실존적"이라는 규칙을 사용하면 foo 함수 항목 유형의 유형은 논리적으로 다음과 같습니다(in 구성 유형 표기):

forall<T: ArgTrait>(exists<R: ReturnTrait>(fn(T) -> R))

순진하게 impl 를 기술적으로만 보편적인 의미 또는 실존적인 의미로만 취급하고 논리가 스스로 작동하도록 내버려 두는 것은 실제로 작동하지 않습니다. 다음 중 하나를 얻을 수 있습니다.

forall<T: ArgTrait, R: ReturnTrait>(fn(T) -> R)

아니면 이거:

exists<T: ArgTrait, R: ReturnTrait>(fn(T) -> R)

그리고 이들 중 어느 것도 논리적 규칙에 의해 우리가 원하는 것으로 축소되지 않습니다. 따라서 궁극적으로 any / some 단일 키워드로 포착할 수 없는 중요한 차이점을 포착합니다. std 에는 보편자를 반환 위치로 지정하려는 합리적인 예도 있습니다. 예를 들어 이 Iterator 메서드는 다음과 같습니다.

fn collect<B>(self) -> B where B: FromIterator<Self::Item>;
// is equivalent to
fn collect(self) -> any FromIterator<Self::Item>;

그리고 impl 와 인수/반환 규칙으로 작성할 방법이 없습니다.

impl 를 갖는 tl;dr 은 문맥상 보편적이거나 실존적임을 나타내지만 실제로는 두 가지 별개의 의미를 부여합니다.


참고로 내 표기법에서 @Rufflewind가 언급한 forall/exists 관계는 다음과 같습니다.

fn(exists<T: Trait>(T)) -> R === forall<T: Trait>(fn(T) -> R)

이는 특성 객체(existentials)가 제네릭(범용)과 동등하다는 개념과 관련이 있지만 이 impl Trait 질문과는 관련이 없습니다.

즉, 나는 더 이상 any / some 강력하게 찬성하지 않습니다. 나는 우리가 말하는 것에 대해 정확하고 싶었고 any / some 는 이론적으로나 시각적으로 훌륭했지만 컨텍스트와 함께 impl 를 사용해도 괜찮을 것입니다. 규칙. 모든 일반적인 경우를 다루고 컨텍스트 키워드 문법 문제를 피하며 나머지는 명명된 유형 매개변수로 이동할 수 있다고 생각합니다.

그런 점에서 보편성의 완전한 일반성을 일치시키려면 결국 이름이 지정된 존재에 대한 구문이 필요할 것이라고 생각합니다. 이 구문은 임의의 where 절과 서명의 여러 위치에서 동일한 존재를 사용할 수 있는 기능을 가능하게 합니다.

요약하면 다음과 같이 만족합니다.

  • impl Trait 는 보편적인 것과 실존적인 것(문맥상) 모두에 대한 약칭입니다.
  • 보편과 실존 모두에 대한 완전한 일반 장수로 명명된 형식 매개변수입니다. (일반적으로 덜 필요합니다.)

단순하게 impl을 기술적으로만 보편적인 의미 또는 실존적인 의미로만 취급하고 논리 자체가 작동하도록 내버려 두는 것은 실제로 작동하지 않습니다. 다음 중 하나를 얻을 수 있습니다.

@solson 나에게 "순진한" 번역은 수량화되는 유형 바로 옆에 실존 수량

~녹(impl MyTrait)~

에 대한 구문 설탕입니다.

~녹(존재하다티)~

이것은 간단한 지역 변환입니다. 따라서 " impl 는 항상 존재합니다" 규칙을 따르는 순진한 번역은 다음과 같은 결과를 낳습니다.

~녹fn(존재티) -> (존재아르 자형)~

그런 다음 함수 인수에서 수량자를 빼면 다음과 같이 됩니다.

~녹~을위한fn(T) -> (존재아르 자형)~

따라서 T 는 항상 자신에 대해 상대적인 존재 지만 전체 함수 유형에 대해 보편적인 상대적인 것으로 나타납니다.


IMO, impl 가 존재 유형의 사실상 키워드가 될 수도 있다고 생각합니다. 미래에는 아마도 다음과 같이 더 복잡한 실존 유형을 구성할 수 있을 것입니다.

~~녹(impl(벡, 티))~ ~

범용 유형과 유사하게(HRTB를 통해)

~녹(for<'a> FnOnce(&'a T))~

@Rufflewind fn(T) -> (exists<R: ReturnTrait>(R))exists<R: ReturnTrait>(fn(T) -> R) 와 논리적으로 동일하지 않기 때문에 해당 보기가 작동하지 않습니다. 이는 impl Trait 반환 유형이 실제로 의미하는 것입니다.

(적어도 존재에 대해 선택된 특정 증인이 관련이 있는 유형 시스템에 일반적으로 적용되는 구성적 논리에서는 그렇지 않습니다. 전자는 함수가 인수를 기반으로 반환할 다른 유형을 선택할 수 있음을 의미하고 후자는 다음이 있음을 의미합니다. impl Trait 의 경우와 같이 함수의 모든 호출에 대한 하나의 특정 유형)

우리도 조금씩 멀어지고 있다는 생각이 듭니다. 상황에 맞는 impl 는 적절한 타협이라고 생각하며 이러한 종류의 정당화에 도달하는 것이 필요하거나 특히 도움이 된다고 생각하지 않습니다(우리는 확실히 이러한 종류의 논리적 연결 측면에서 규칙을 가르치지 않을 것입니다 ).

@solson

(T → ∃R. f(R))  ⥇  ∃R. T → f(R)

일반적으로 다음과 같이 유지됩니다.

(∃R. T → f(R))  →   T → ∃R. f(R)
(∀A. g(A) → T)  ↔  ((∃A. g(A)) → T)

마지막 것은 제네릭으로 인수의 존재를 재해석하는 역할을 합니다.

편집: 죄송합니다. (∀A. g(A) → T) → (∃A. g(A)) → T 유효합니다.

impl Trait 확장 및 안정화에 대한 자세한 제안과 함께 RFC 를 게시

https://github.com/rust-lang/rfcs/pull/1951 이 승인되었다는 점은 주목할 가치가 있습니다.

이에 대한 현재 상황은 어떻습니까? 우리는 착륙한 RFC를 가지고 있고 초기 구현을 사용하는 사람들이 있지만 어떤 항목이 할 일인지 확실하지 않습니다.

#43869에서 -> impl Trait 함수가 완전히 발산하는 바디를 지원하지 않는다는 것을 발견했습니다:

fn do_it_later_but_cannot() -> impl Iterator<Item=u8> { //~ ERROR E0227
    unimplemented!()
}

이것은 예상된 것입니까( !Iterator 암시하지 않기 때문에), 아니면 버그로 간주됩니까?

유추된 유형을 정의하는 것은 어떻습니까? 반환 값으로 사용할 수 있을 뿐만 아니라 현재 유형을 사용할 수 있는 모든 것(내 생각에는)은 무엇입니까?
다음과 같은 것:
type Foo: FnOnce() -> f32 = #[infer];
또는 키워드 사용:
infer Foo: FnOnce() -> f32;

Foo 유형은 리턴 유형, 매개변수 유형 또는 유형이 사용될 수 있는 다른 모든 것으로 사용될 수 있지만, type은 두 경우 모두 FnOnce() -> f32 를 구현합니다. 예를 들어 다음은 컴파일되지 않습니다.

infer Foo: FnOnce() -> f32;

fn return_closure() -> Foo {
    || 0.1
}

fn return_closure2() -> Foo {
    || 0.2
}

fn main() {
    println!("{:?}, {:?}", return_closure()(), return_closure2()());
}

return_closurereturn_closure2 의 반환 유형이 모두 FnOnce() -> f32 임에도 불구하고 실제로 유형이 다르기 때문에 컴파일되어서는 안 됩니다. . 위의 내용을 컴파일하려면 두 가지 다른 유추 유형을 정의해야 합니다.

infer Foo: FnOnce() -> f32;
infer Foo2: FnOnce() -> f32; //Added this line

fn return_closure() -> Foo {
    || 0.1
}

fn return_closure2() -> Foo2 { //Changed Foo to Foo2
    || 0.2
}

fn main() {
    println!("{:?}, {:?}", return_closure()(), return_closure2()());
}

infer 키워드가 무엇을 하는지 미리 알지 못하더라도 코드를 보고 나면 여기서 무슨 일이 일어나고 있는지 매우 분명하고 매우 유연합니다.

추론 키워드(또는 매크로)는 기본적으로 컴파일러에게 사용 위치에 따라 유형이 무엇인지 알아내도록 지시합니다. 컴파일러가 유형을 유추할 수 없는 경우 오류가 발생합니다. 이는 유형이 어떤 유형이어야 하는지를 좁힐 수 있는 정보가 충분하지 않을 때 발생할 수 있습니다(예를 들어, 유추된 유형이 어디에도 사용되지 않는 경우). 특정 경우를 경고로 만드는 것이 더 나을 수도 있습니다) 또는 사용되는 모든 곳에 맞는 유형을 찾는 것이 불가능한 경우(위의 예와 같이).

@cramertj 아 그래서 이 문제가 잠잠

그래서 @cramertj마주한 늦은 바인딩 영역의 문제를 해결하는 것이 가장 좋을 것이라고 생각하는 방법에 대해 질문했습니다. 내 생각은 우리가 anonymous type Foo 모델을 기대하고 시도하기 위해 구현을 약간 "재조정"하고 싶다는 것입니다.

컨텍스트의 경우 아이디어는 대략 다음과 같습니다.

fn foo<'a, 'b, T, U>() -> impl Debug + 'a

(일종의) 이런 식으로 desugared 것입니다

anonymous type Foo<'a, T, U>: Debug + 'a
fn foo<'a, 'b, T, U>() -> Foo<'a, T, U>

이 형식에서 Foo 대한 인수로 나타나기 때문에 캡처된 일반 매개변수를 볼 수 있습니다. 특히 'b 는 캡처되지 않습니다. 어쨌든 TU 유형 매개변수는 항상 있습니다.

어쨌든 현재 컴파일러에서 impl Debug 참조가 있는 경우 이 익명 유형을 효과적으로 나타내는 def-id를 만듭니다. 그런 다음 일반 매개변수를 계산하는 generics_of 쿼리가 있습니다. 지금은 "묶는" 컨텍스트와 동일한 결과를 반환합니다. 즉, foo 함수입니다. 이것이 우리가 바꾸고자 하는 것입니다.

"다른 쪽"에서, 즉 foo 의 서명에서 impl FooTyAnon 냅니다. 이것은 기본적으로 맞습니다. TyAnon 는 위의 디슈가링에서 볼 수 있는 Foo 대한 참조를 나타냅니다. 그러나 우리가 이 유형에 대한 "substs"를 얻는 방법은 "identity" 함수를 사용하는 것인데, 이는 명백히 잘못되었거나 적어도 일반화되지는 않습니다.

특히 여기에서 일종의 "네임스페이스 위반"이 발생합니다. 항목에 대한 "identity" 부분을 생성하면 일반적으로 해당 항목을 유형 검사할 때 사용하는 대체 항목을 제공합니다. 즉, 범위 내의 모든 일반 매개변수를 사용합니다. 그러나 이 경우 foo() 함수 내부에 표시되는 Foo 대한 참조를 생성하므로 Substs 표시되는 foo() 의 일반 매개변수를 원합니다. Foo 아닙니다. 이것은 지금 당장 그것들이 하나이고 동일하기 때문에 작동하지만 실제로는 옳지 않습니다.

우리가 해야 할 일은 다음과 같다고 생각합니다.

첫째, Foo 의 제네릭 유형 매개변수(즉, 익명 유형 자체)를 계산할 때 새로운 제네릭 세트를 구성하기 시작합니다. 당연히 유형도 포함됩니다. 그러나 평생 동안 우리는 특성 경계를 넘어 그 안에 나타나는 각 영역을 식별했습니다. 범위의 모든 영역에 def-id가 있는 것은 아니기 때문에 def-id를 누적하고 싶지 않다는 점을 제외하고는 cramertj가 작성한 이 기존 코드 와 매우 유사합니다.

우리가 하고 싶은 것은 표시되는 영역 집합을 누적하여 정렬하고 foo() 의 관점에서 해당 영역의 값을 추적 FreeRegion 라는 개념이 있었는데 거의 효과가 있었을 것입니다. 하지만 더 이상 조기 바인딩된 항목에는 FreeRegion 를 사용하지 않고 후기 바인딩된 항목에만 사용합니다.)

아마도 가장 쉽고 최상의 옵션은 Region<'tcx> 를 사용하는 것이지만 도입된 바인더를 "취소"하려면 debruijn 인덱스 깊이를 이동해야 합니다. 이것이 아마도 최선의 선택일 것입니다.

따라서 기본적으로 visit_lifetime 에서 콜백을 받을 때 초기 깊이로 표현된 Region<'tcx> 로 변환합니다(바인더를 통과할 때 추적해야 함). 우리는 그것들을 벡터로 축적하여 중복을 제거할 것입니다.

완료되면 두 가지가 있습니다.

  • 먼저 벡터의 각 영역에 대해 일반 영역 매개변수를 생성해야 합니다. 그들은 모두 익명의 이름이나 무엇이든 가질 수 있습니다. 별로 중요하지 않습니다. (아마도 우리는 def-id 또는 뭔가가 필요하지만...? RegionParameterDef 데이터 구조를 살펴봐야 합니다...) .
  • 둘째, 벡터의 영역은 "substs"에 사용하려는 것이기도 합니다.

알겠습니다. 비밀스럽다면 죄송합니다. 나는 그것을 더 명확하게 말하는 방법을 잘 이해할 수 없습니다. 확실하지 않은 부분이 있습니다. 지금은 영역 처리가 매우 복잡하다고 생각합니다. 그래서 더 균일하게 만들기 위해 리팩토링하는 방법이 있을까요? @eddyb 가 여기에서 몇 가지 생각을 하고 있다는 겠습니다 . ;)

@nikomatsakis 나는 그 많은 부분이 내가 @cramertj 에게 말한 것과 비슷하다고 생각하지만 더 구체화되었습니다!

나는 실존적인 impl Trait 에 대해 생각하고 있었고 조심스럽게 진행해야한다고 생각하는 이상한 경우를 만났습니다. 다음 기능을 고려하십시오.

trait Foo<T> { }
impl Foo<()> for () { }
fn foo() -> impl Foo<impl Debug> {
  ()
}

플레이 에서 확인할 수 있듯이 이 코드는 오늘 컴파일됩니다. 그러나 우리가 무슨 일이 일어나고 있는지 파헤쳐 보면 저와 관련된 "순방향 호환성" 위험이 있는 것을 강조합니다.

특히 여기에서 반환되는 유형( () )을 추론하는 방법이 명확합니다. impl Debug 매개변수의 유형을 추론하는 방법은 명확하지 않습니다. 즉, 이 반환 값을 -> ?T ?T: Foo<?U> 와 같은 것으로 생각할 수 있습니다. ?T = () 라는 사실에 근거하여 ?T?U 의 값을 추론해야 합니다.

지금 당장은 하나의 impl만 존재한다는 사실을 활용하여 이를 수행합니다. 그러나 이것은 취약한 속성입니다. 새 impl이 추가되면 코드는 더 이상 컴파일되지 않습니다. 이제 ?U 가 무엇인지 고유하게 결정할 수 없기 때문입니다.

이것은 Rust의 많은 시나리오에서 발생할 수 있습니다. 이는 충분히 우려되지만 직교합니다. 하지만 impl Trait 경우에는 다른 것이 있습니다. impl Trait 경우 사용자가 유형 주석을 추가하여 추론을 안내할 방법이 없습니다! 우리는 실제로 그런 식으로 계획을 가지고 있지 않습니다. 유일한 해결책은 fn 인터페이스를 impl Foo<()> 또는 다른 명시적인 것으로 변경하는 것입니다.

미래에는 abstract type 하여 사용자가 명시적으로 숨겨진 값(또는 _ 사용하여 불완전한 힌트)을 제공하도록 허용하는 것을 상상할 수 있습니다. 그러면 대략적으로 유지하면서 추론에 도움이 될 수 있습니다. 동일한 공개 인터페이스

abstract type X: Debug = ();
fn foo() -> impl Foo<X> {
  ()
}

그래도 연관된 유형 바인딩을 제외하고(예: impl Iterator<Item = impl Debug> 는 이러한 모호성을 겪지 않음) 실존적 impl Trait의 "중첩" 사용을 안정화하는 것을 피하는 것이 현명하다고 생각합니다.

impl Trait의 경우 사용자가 유형 주석을 추가하여 추론을 안내할 방법이 없습니다! 우리는 실제로 그런 식으로 계획을 가지고 있지 않습니다.

아마도 UFCS처럼 보일 수 있습니까? 예를 들어 <() as Foo<()>> -- as 처럼 유형을 변경 하지 않고 단지 명확하게 합니다. 이것은 :: 등이 뒤따를 것으로 예상하므로 현재 유효하지 않은 구문입니다.

방금 Fn 대한 impl Trait 을 사용한 유형 추론에 관한 흥미로운 사례를 찾았습니다.
다음 코드 는 잘 컴파일됩니다 .

fn op(s: &str) -> impl Fn(i32, i32) -> i32 {
    match s {
        "+" => ::std::ops::Add::add,
        "-" => ::std::ops::Sub::sub,
        "<" => |a,b| (a < b) as i32,
        _ => unimplemented!(),
    }
}

Sub-line을 주석 처리 하면 컴파일 오류가 발생합니다 .

error[E0308]: match arms have incompatible types
 --> src/main.rs:4:5
  |
4 | /     match s {
5 | |         "+" => ::std::ops::Add::add,
6 | | //         "-" => ::std::ops::Sub::sub,
7 | |         "<" => |a,b| (a < b) as i32,
8 | |         _ => unimplemented!(),
9 | |     }
  | |_____^ expected fn item, found closure
  |
  = note: expected type `fn(_, _) -> <_ as std::ops::Add<_>>::Output {<_ as std::ops::Add<_>>::add}`
             found type `[closure@src/main.rs:7:16: 7:36]`
note: match arm with an incompatible type
 --> src/main.rs:7:16
  |
7 |         "<" => |a,b| (a < b) as i32,
  |                ^^^^^^^^^^^^^^^^^^^^

error: aborting due to previous error

@oberien 이것은 impl Trait 와 관련이 없는 것 같습니다. 일반적으로 추론에 해당합니다. 다음과 같이 예제를 약간 수정해 보십시오.

fn main() {
    let _: i32 = (match "" {
        "+" => ::std::ops::Add::add,
        //"-" => ::std::ops::Sub::sub,
        "<" => |a,b| (a < b) as i32,
        _ => unimplemented!(),
    })(5, 5);
}

현재 폐쇄된 것 같습니다.

elision과 상호 작용할 때의 ICE

이 문제나 토론에 나열되지 않은 한 가지는 호출자가 제공하지 않는 클로저와 생성자를 구조체 필드에 저장하는 기능입니다. 지금 당장은 가능하지만 보기 흉해 보입니다. 각 클로저/생성기 필드에 대한 유형 매개변수를 구조체에 추가한 다음 생성자 함수의 서명에서 해당 유형 매개변수를 impl FnMut/impl Generator 바꿔야 합니다. 다음은 예입니다 . 작동합니다. 꽤 멋집니다! 그러나 그것은 많은 것을 남깁니다. 유형 매개변수를 제거할 수 있다면 훨씬 더 좋을 것입니다.

struct Counter(impl Generator<Yield=i32, Return=!>);

impl Counter {
    fn new() -> Counter {
        Counter(|| {
            let mut x: i32 = 0;
            loop {
                yield x;
                x += 1;
            }
        })
    }
}

RFC 2071을 올바르게 읽고 이해했다면 impl Trait 가 올바른 방법이 아닐 수 있습니다. 아마도 추상 유형일 것입니다. 우리에게 필요한 것은 실제 유형( [generator@src/main.rs:15:17: 21:10 _] )을 유추할 수 있도록 구조체 정의에 작성할 수 있는 것입니다.

@mikeyhew 추상 유형은 실제로 작동할 것으로 기대하는 방식입니다. 구문은 대략 다음과 같습니다.

abstract type MyGenerator: Generator<Yield = i32, Return = !>;

pub struct Counter(MyGenerator);

impl Counter {
    pub fn new() -> Counter {
        Counter(|| {
            let mut x: i32 = 0;
            loop {
                yield x;
                x += 1;
            }
        })
    }
}

내 구조체에 넣고 싶은 다른 사람의 impl Generator 인데 내가 사용할 abstract type 을 만들지 않은 경우 대체 경로가 있습니까?

@scottmcm 여전히 abstract type 선언할 수 있습니다.

// library crate:
fn foo() -> impl Generator<Yield = i32, Return = !> { ... }

// your crate:
abstract type MyGenerator: Generator<Yield = i32, Return = !>;

pub struct Counter(MyGenerator);

impl Counter {
    pub fn new() -> Counter {
        let inner: MyGenerator = foo();
        Counter(inner)
    }
}

@cramertj 잠깐, 추상 유형은 이미 야간에?! 홍보는 어디있나요?

@alexreg 아니요, 그렇지 않습니다.

편집: 안녕하세요, 미래에서 온 방문자 여러분! 아래 문제 가 해결되었습니다.


#47348에 나오는 이 펑키한 사용 사례에 주의를 기울이고 싶습니다.

use ::std::ops::Sub;

fn test(foo: impl Sub) -> <impl Sub as Sub>::Output { foo - foo }

이와 같이 impl Trait 에 대한 프로젝션을 반환하는 것도 허용되어야 합니까? (현재 __it is.__이기 때문에)

이와 같은 사용법에 대한 토론을 찾을 수 없었고 이에 대한 테스트 사례도 찾을 수 없었습니다.

@ExpHP 흠. impl Foo<impl Bar> 가 문제가 있는 것과 같은 이유로 문제가 있는 것 같습니다. 기본적으로 우리는 문제의 유형에 대한 실제 제약 이 없습니다.

impls에서 "제약된 유형 매개변수"에 대한 논리를 재사용하고 싶다고 생각합니다. 요컨대, 반환 유형을 지정하면 impl Sub "제약"해야 합니다. 내가 언급하는 기능은 다음과 같습니다.

https://github.com/rust-lang/rust/blob/a0dcecff90c45ad5d4eb60859e22bb3f1b03842a/src/librustc_typeck/constrained_type_params.rs#L89 -L93

체크박스를 좋아하는 사람들을 위한 약간의 분류:

  • #46464 완료 -> 확인란
  • #48072 완료 -> 확인란

@rfcbot fcp 병합

저는 conservative_impl_traituniversal_impl_trait 기능을 안정화 할 것을 제안합니다. 여기에는 보류 중인 변경 사항이 있습니다(https://github.com/rust-lang/rust/issues/46541 수정).

현재 의미 체계를 문서화하는 테스트

이러한 기능에 대한 테스트는 다음 디렉토리에서 찾을 수 있습니다.

실행 통과/임플 특성
UI/impl-trait
컴파일 실패/impl-trait

구현 중 해결된 질문

impl Trait 의 구문 분석에 대한 세부 정보는 RFC 2250 에서 해결되었으며 https://github.com/rust-lang/rust/pull/45294 에서 구현되었습니다

impl Trait 는 모호성을 방지하기 위해 중첩되지 않은 유형 위치 및 특정 자격을 갖춘 경로 위치에서 금지되었습니다. 이것은 https://github.com/rust-lang/rust/pull/48084 에서 구현되었습니다

나머지 불안정한 기능

이 안정화 후에는 인수 위치에 impl Trait 를 사용하고 특성이 아닌 함수의 위치를 ​​반환할 수 있습니다. 그러나, 사용 impl Trait 어디에서 Fn 구문은 여전히 미래의 설계 반복을 허용하기 위해 허용되지 않습니다. 또한 인수 위치에 impl Trait 를 사용하는 함수의 유형 매개변수를 수동으로 지정하는 것은 허용되지 않습니다.

팀원 @cramertj 가 이를 병합할 것을 제안했습니다. 다음 단계는 태그가 지정된 나머지 팀이 검토하는 것입니다.

  • [x] @아투론
  • [x] @cramertj
  • [x] @eddyb
  • [x] @nikomatsakis
  • [x] @nrc
  • [x] @pnkfelix
  • [x] @withoutboats

현재 나열된 우려 사항이 없습니다.

과반수의 검토자가 승인하고 반대하지 않으면 최종 의견 제출 기간이 시작됩니다. 이 프로세스의 어느 시점에서도 제기되지 않은 주요 문제를 발견하면 알려주세요!

태그가 지정된 팀 구성원이 나에게 줄 수 있는 명령에 대한 정보는 이 문서 를 참조하십시오.

이 안정화 후에는 인수 위치에 impl Trait을 사용하고 특성이 아닌 함수의 위치를 ​​반환할 수 있습니다. 그러나 Fn 구문의 모든 위치에서 impl Trait을 사용하는 것은 향후 디자인 반복을 위해 여전히 허용되지 않습니다. 또한 인수 위치에서 impl Trait를 사용하는 함수의 유형 매개변수를 수동으로 지정하는 것은 허용되지 않습니다.

사용의 상태 무엇입니까 impl Trait 그 문제에 관해서, 특성 함수에 인수 / 반환 위치를, 또는 FN 구문은?

@alexreg 특성의 반환 위치 impl Trait 는 RFC에서 차단되지만 RFC 2071은 일단 구현되면 유사한 기능을 허용합니다. 특성의 인수 위치 impl Trait 는 내가 알고 있는 기술적 기능에서 차단되지 않았지만 RFC에서 명시적으로 허용되지 않았으므로 당분간 생략했습니다.

impl Trait Fn 구문의 인수 위치에 있는 T: Fn(impl Trait)T: for<X: Trait> Fn(X) desugar해야 한다고 생각하기 때문입니다. impl Trait Fn 구문의 반환 위치에 있는

@cramertj 네, 업데이트해주셔서 감사합니다. 아무 것도 차단되지 않은 이 두 가지 기능이 논의 후 곧 진행되는 것을 볼 수 있기를 바랍니다. 내가 실수하지 않는 한, 디슈가링은 인수 위치에서 foo: T 인수가 의미가 있습니다. 여기서 T: Traitfoo: impl Trait 와 같습니다.

우려 사항: https://github.com/rust-lang/rust/issues/34511#issuecomment -322340401은 여전히 ​​동일합니다. 다음을 허용할 수 있습니까?

fn do_it_later_but_cannot() -> impl Iterator<Item=u8> { //~ ERROR E0227
    unimplemented!()
}

@kennytm 아니요, 현재로서는 불가능합니다. 그 함수는 ! 반환합니다. 이는 제공한 특성을 구현하지 않으며 이를 적절한 유형으로 변환하는 메커니즘도 없습니다. 이것은 불행한 일이지만 지금 당장 고칠 수 있는 쉬운 방법은 없습니다( ! 대한 더 많은 특성 구현 제외). 또한 작동하도록 하면 엄격하게 더 많은 코드를 컴파일할 수 있으므로 향후 수정하기 위해 이전 버전과도 호환됩니다.

터보피시 문제는 절반만 해결되었습니다. 우리는 최소한 impl Trait 에 대해 효과적인 공개 함수의 인수에 대해 경고해야 합니다. 인수의 impl Trait 가 public check 의 새로운 하십시오 .

동기는 명시적 제네릭 인수를 impl Trait 로 변경하여 라이브러리가 사용자의 터보피쉬를 손상시키는 것을 방지하는 것입니다. 우리는 아직 libs가 브레이킹 체인지인지 아닌지 알 수 있는 좋은 참조 가이드가 없으며 테스트에서 이를 포착할 가능성은 거의 없습니다. 이 문제는 충분히 논의되지 않았습니다. 우리가 완전히 결정하기 전에 안정화하려면 최소한 총을 lib 작성자의 발에서 멀리 떨어뜨려야 합니다.

동기는 인수를 명시적 제네릭에서 impl Trait 로 변경하여 라이브러리가 사용자의 터보피쉬를 손상시키는 것을 방지하는 것입니다.

이런 일이 일어나기 시작하고 사람들이 불평하기 시작하면 현재 의심스러운 사람들이 impl Trait 이 터보피시와 함께 유형 인수를 명시적으로 제공하는 것을 지원해야 한다고 확신하게 되기를 바랍니다.

@leodasvacas

터보피시 문제는 절반만 해결되었습니다. 우리는 최소한 인수의 impl Trait이 공개 확인의 새로운 private에 대한 비공개 유형임을 고려하여 효과적인 공개 함수의 인수에서 impl Trait에 대해 경고해야 합니다.

동의하지 않습니다. 해결되었습니다. 당분간 이러한 기능에 대한 터보피시를 완전히 허용하지 않습니다. 명시적 일반 매개변수 대신 impl Trait 를 사용하도록 공용 함수의 서명을 변경하는 것은 주요 변경 사항입니다.

앞으로 이러한 기능에 대해 터보피시를 허용하면 impl Trait 유형이 아닌 매개변수만 지정할 수 있습니다.

:bell: 이제 최종 댓글 기간에 접어들었습니다 . :벨:

https://github.com/rust-lang/rust/pull/49041이 착륙할 때까지 안정화 하고 싶지 않다고 덧붙였습니다. (하지만 곧 그렇게 되기를 바랍니다.)

따라서 #49041에는 #46541에 대한 수정 사항이 포함되어 있지만 해당 수정 사항은 내가 예상한 것보다 더 큰 영향을 미칩니다. 예를 들어 컴파일러가 지금 부트스트랩하지 않습니다. 그리고 여기에서 올바른 과정에 대해 잠시 멈추고 있습니다. #49041의 문제는 우리가 의도하지 않은 수명이 실수로 누출되도록 할 수 있다는 것입니다. 이것이 컴파일러에서 어떻게 나타나는지 보여줍니다. 다음과 같은 방법이 있을 수 있습니다.

impl TyCtxt<'cx, 'gcx, 'tcx>
where 'gcx: 'tcx, 'tcx: 'cx
{
    fn foos(self) -> impl Iterator<Item = &'tcx Foo> + 'cx {
        /* returns some type `Baz<'cx, 'gcx, 'tcx>` that captures self */
    }
}

여기서 중요한 점은 TyCtxt 가 w/r/t 'tcx'gcx 가 불변이므로 반환 유형에 나타나야 한다는 것입니다. 그러나 'cx'tcx 만 impl 특성 경계에 나타나므로 두 수명만 "캡처"해야 합니다. 이전 컴파일러는 'gcx: 'cx 때문에 이것을 수락했지만, 우리가 염두에 두고 있는 디슈가링에 대해 생각한다면 그것은 정말로 옳지 않습니다. 그 desugaring은 다음과 같은 추상 유형을 생성합니다.

abstract type Foos<'cx, 'tcx>: Iterator<Item = &'tcx Foo> + 'cx;

그러나 이 추상 유형의 값은 Baz<'cx, 'gcx, 'tcx> -- 하지만 'gcx 는 범위에 없습니다!

여기서 해결 방법은 경계에서 'gcx 이름을 지정해야 한다는 것입니다. 이것은 일종의 성가신 일입니다. 우리는 'cx + 'gcx 사용할 수 없습니다. 더미 특성을 만들 수 있습니다.

trait Captures<'a> { }
impl<T: ?Sized> Captures<'a> for T { }

그런 다음 impl Iterator<Item = &'tcx Foo> + Captures<'gcx> + Captures<'cx> 와 같은 것을 반환합니다.

내가 언급하는 것을 잊은 것: 선언된 반환 유형이 dyn Iterator<Item = &'tcx Foo> + 'cx 이면 dyn 유형은 수명을 지울 것으로 예상되기 때문에 괜찮을 것입니다. 따라서 나는 당신이 아무것도 문제를 할 수없는 추정, 어떤 unsoundness 여기 가능하다 생각하지 않는다 impl Trait 가능하지 않을 것이라고 dyn Trait .

추상 유형의 값이 exists<'gcx> Baz<'cx, 'gcx, 'tcx> 와 유사한 실존적이라는 아이디어를 막연하게 상상할 수 있습니다.

그러나 보수적인 부분 집합(위의 fns를 제외함)을 안정화하고 나중에 생각하는 방식을 결정한 후 이를 가능한 확장으로 다시 검토하는 것이 괜찮은 것 같습니다.

업데이트 :에 대한 내 의미를 명확히하기 위해 dyn 특성 : 나는 말하고 그들이 할 수있는 "숨기기"와 같은 평생 'gcx 너무 오래 바운드로 ( 'cx , 여기) 것을 보장 'gcxdyn Trait 가 사용되는 모든 위치에 계속 표시됩니다.

@nikomatsakis 흥미로운 바뀌었다고 생각하지 않습니다. 즉, 모든 관련 수명이 반환 유형에서만 명확하기를 원한다는 것입니다.

Captures 특성은 이 상황에 대한 좋은 가벼운 접근 방식인 것 같습니다. 당분간 불안정한 상태로 std::marker 들어갈 것 같은데요?

@nikomatsakis 귀하의 후속 의견은 이 경우 'gcx 를 생략할 것으로 예상하는 이유를 이해하기 위해 여기에 모든 부분을 통합하지 않았다는 것을 깨달았습니다. 즉, 'gcx 는 클라이언트의 관점에서 "관련 수명". 어쨌든 보수적으로 시작하는 것이 좋습니다.

내 개인적인 의견은 https://github.com/rust-lang/rust/issues/46541 이 실제로 버그가 아니라는 것입니다. 이는 내가 예상하는 동작입니다. 어떻게 문제가 생길 수 있는지 알 수 없습니다. 그리고 주변에서 일하는 것은 고통스럽습니다. IMO는 구현 유형 반환 할 수 있어야 Trait 와 들보 다 오래 남았습니다 수명을 'aimpl Trait + 'a , 포함 된 다른 어떤 생애에 상관없이. 그러나 @rust-lang/lang이 선호하는 경우 시작하기 위해 보다 보수적인 접근 방식을 안정화하는 것이 좋습니다.

(명확히 해야 할 또 다른 사항: #49041 수정으로 오류가 발생하는 유일한 경우는 숨겨진 유형이 'gcx 누락된 수명과 관련하여 불변일 때이므로 비교적 드물게 발생합니다.)

@cramertj

내 개인적인 의견은 #46541이 실제로 버그가 아니라는 것입니다. 예상한 동작입니다. 어떻게 문제가 생길 수 있는지 알 수 없으며 해결하기가 매우 어렵습니다.

나는 그 POV에 공감하지만, 우리가 그것을 어떻게 설탕을 제거해야 하는지 이해하지 못하는 무언가를 안정화하는 것을 꺼립니다(예를 들어, 실존적 수명에 대한 모호한 개념에 의존하는 것처럼 보이기 때문에).

@rfcbot은 다중 반품 사이트와 관련이 있습니다.

실존적 임프 특성에 대한 마지막 관심사를 등록하고 싶습니다. impl 특성을 사용하려는 경우의 상당 부분에서 실제로는 둘 이상의 유형을 반환하고 싶습니다. 예를 들어:

fn foo(empty: bool) -> impl Iterator<Item = u32> {
    if empty { None.into_iter() } else { &[1, 2, 3].cloned() }
}

물론, 이것은 오늘날 작동하지 않으며 작동하게 하는 것은 확실히 범위를 벗어납니다. 그러나 IMPL 특성 그 방법은 우리가 효율적으로 (즉, 구문) 지금까지 일에 문을 닫는, 작동합니다. 이는 -- 현재 -- 여러 반품 사이트에서 제약 조건을 누적할 수 있기 때문입니다.

fn foo(empty: bool) -> (impl Debug, impl Debug) {
    if empty { return (22, Default::default()); }
    return (Default::default(), false);
}

여기서 유추된 유형은 (i32, bool) . 여기서 첫 번째 returni32 부분을 ​​제한하고 두 번째 return bool 부분을 ​​제한합니다.

이것은 우리가 두 개의 return 문이 통합되지 않는 경우를 결코 지원할 수 없다는 것을 의미합니다(제 첫 번째 예에서와 같이) -- 그렇지 않으면 그렇게 하는 것이 매우 성가실 것입니다.

return (일반적으로 제약 조건의 각 소스)가 독립적으로 완전히 지정되어야 하는 제약 조건을 넣어야 하는지 궁금합니다. (그리고 우리는 사실 후에 그들을 통합합니까?)

이렇게 하면 제 두 번째 사례가 불법이 되고 미래의 어느 시점에서 첫 번째 사례를 잠재적으로 지원할 수 있는 여지가 생깁니다.

@rfcbot은 다중 반품 사이트를 해결합니다.

그래서 #rust-lang에 대해 @cramertj나눴 습니다 . 우리는 impl Trait 대해 "조기 반환"을 불안정하게 만드는 아이디어에 대해 논의하고 있었습니다. 그래서 결국 변경할 수 있습니다.

그들은 이러한 종류의 구문을 명시적으로 선택하는 것이 더 낫다고 주장했습니다. 특히 다른 경우(예: let x: impl Trait = if { ... } else { ... } )가 필요하고 우리가 기대할 수 없기 때문입니다. 그것들을 모두 암묵적으로 처리합니다(확실히 아닙니다).

나는 이것이 꽤 설득력이 있다고 생각한다. 그 전에는 여기에서 옵트인 구문이 있을 것이라고 가정했지만 성급하게 문을 닫지 않았는지 확인하고 싶었습니다. 결국, "다이나믹 심"을 삽입해야 하는 시점을 설명하는 것은 다소 까다롭습니다.

@nikomatsakis 정보가 부족한 내 의견: 런타임에 가능한 여러 유형 중 하나를 반환하는 함수를 활성화하는 것이 유용할 수 있지만 단일 유형에 대한 정적 반환 유형 추론에 대해 동일한 구문을 사용하는 것을 꺼립니다. 일부 런타임 결정이 내부적으로 필요한 상황을 허용합니다(방금 "동적 심"이라고 부름).

첫 번째 foo 예제는 문제를 이해하는 한 (1) boxed + type-erased Iterator<Item = u32> 또는 (2) std::option::Iter 합계 유형으로 해결할 수 있습니다. std::slice::Iter , 차례로 Iterator 구현을 파생합니다. 토론에 대한 일부 업데이트가 있었고(즉, 지금 IRC 로그를 읽고 있음) 이해하기가 점점 더 어려워지기 때문에 짧게 유지하려고 합니다. dyn 라고 부르는 것은 이상적이지 않을 수 있습니다.

뻔뻔한 플러그와 기록을 위한 작은 메모: 다음을 사용하여 "익명" 합계 유형 및 제품을 쉽게 얻을 수 있습니다.

@Centril 네, CoprodInjector::inject 가 작동하려면 결과 유형을 유추할 수 있어야 하며 이는 일반적으로 결과 유형의 이름을 지정하지 않고는 불가능합니다(예: -> Coprod!(A, B, C) ). 이름을 지정할 수 없는 유형으로 작업하는 경우가 종종 있으므로 -> Coprod!(impl Trait, impl Trait, impl Trait) 가 필요합니다. 이는 어떤 변형이 어떤 impl Trait 유형을 포함해야 하는지 모르기 때문에 추론에 실패합니다.

@cramertj 매우 사실입니다(참고: 각 "변형"은 완전히 이름을 지정할 수 없지만 부분적으로만 예: Map<Namable, Unnameable> ).

enum impl Trait 아이디어는 https://internals.rust-lang.org/t/pre-rfc-anonymous-enums/5695 에서 이전에 논의되었습니다.

@센트릴 네,

fn foo(x: Foo) -> impl Future<Item = (), Error = Never> {
    match x {
        Foo::Bar => do_request().and_then(|res| ...).left().left(),
        Foo::Baz => do_other_thing().and_then(|res| ...).left().right(),
        Foo::Boo => do_third_thing().and_then(|res| ...).right(),
    }
}

@cramertj 나는 익명 열거형이 enum impl Trait 와 비슷하다고 말하지 않을 것입니다. 왜냐하면 우리는 X: Tr && Y: Tr(X|Y): Tr (반대 예: Default 특성). 따라서 라이브러리 작성자는 수동으로 impl Future for (X|Y|Z|...) 합니다.

@kennytm 아마도 우리는 익명 열거형에 대한 일부 특성 impl을 자동 생성하기를 원할 것이므로 기본적으로 동일한 기능처럼 보입니다.

익명 열거가 (ㅎ) 이름을 지정할 수 있습니다 있기 때문에 경우, @cramertj Default IMPL가 생성됩니다 (i32|String) , 우리는 쓸 수있을 것입니다 <(i32|String)>::default() . OTOH <enum impl Default>::default() 단순히 컴파일되지 않으므로 자동 생성에 관계없이 전혀 호출할 수 없기 때문에 여전히 안전합니다.

그럼에도 불구하고 자동 생성이 여전히 enum impl Trait 문제를 일으킬 수 있는 경우가 있습니다. 고려하다

pub trait Rng {
    fn next_u32(&mut self) -> u32;
    fn gen<T: Rand>(&mut self) -> T where Self: Sized;
    fn gen_iter<'a, T: Rand>(&'a mut self) -> Generator<'a, T, Self> where Self: Sized;
}

mut rng: (XorShiftRng|IsaacRng) 가 있으면 rng.next_u32() 또는 rng.gen::<u64>() 계산할 수 있는 것은 지극히 정상입니다. 그러나 자동 생성은 (Generator<'a, u16, XorShiftRng>|Generator<'a, u16, IsaacRng>) 만 생성할 수 있기 때문에 rng.gen_iter::<u16>() 생성할 수 없지만 실제로 원하는 것은 Generator<'a, u16, (XorShiftRng|IsaacRng)> 입니다.

(아마도 컴파일러는 Sized 검사처럼 안전하지 않은 위임 호출을 자동으로 거부할 수 있습니다.)

FWIW 이 기능은 튜플(물론 가상의 익명 enum 상응하는 익명 struct 임)보다 클로저에 더 가깝다는 생각이 듭니다. 이러한 것들이 "익명"인 방식은 다릅니다.

익명 structenum (튜플 및 "분리")의 경우 "익명"은 "구조적"("명목"과 반대) 유형의 의미에서 -- 그들은' 다시 내장되고 구성 요소 유형에 대해 완전히 일반화되며 모든 소스 파일에 명명된 선언이 아닙니다. 그러나 프로그래머는 여전히 그것들을 작성하고 다른 유형처럼 사용하며, 그것들에 대한 특성 구현은 평소와 같이 명시적으로 작성되며 특별히 마술적이지 않습니다(내장 구문이 있고 다른 유형이 '가변적'인 것을 제외하고는). 아직 할 수 없습니다). 어떤 의미에서, 그들은 이름을 가지고 있지만, 대신에 숫자가되는, 자신의 '이름'그들 (괄호 및 쉼표)를 작성하는 데 사용하는 구문입니다.

반면에 클로저는 이름이 비밀 이라는 점에서 익명입니다. 컴파일러는 당신이 하나를 작성할 때마다 새로운 이름을 가진 새로운 유형을 생성하며, 그 이름이 무엇인지 찾거나 원하더라도 참조할 방법이 없습니다. 컴파일러는 이 비밀 유형에 대해 한두 가지 특성을 구현하며, 이 특성과 상호 작용할 수 있는 유일한 방법은 이러한 특성을 통해서입니다.

의 다른 지점에서 다른 유형을 반환 할 수 있다는 if 뒤에, impl Trait 가까이 후자 보인다 - 컴파일러는 암시 적으로 여러 가지를 보유하는 유형, 구현 요구 특성을 생성 적절한 것에 파견하기 위해 그것에 디스패치하고, 프로그래머는 결코 그 유형이 무엇인지 기록하거나 보지 않으며, 그것을 참조할 수도 없고 원할 실질적인 이유도 없습니다.

(사실 이 기능은 Fn 대한 기존 클로저 구문이 다른 특성에 대한 가상의 "객체 리터럴"과 관련이 있다고 느껴집니다 . 즉, 단일 람다 표현식 대신 범위의 변수를 사용하여 주어진 특성의 각 메서드( self 가 암시적임)를 구현하고 컴파일러는 upvar를 보유할 익명 유형을 생성하고 이에 대해 주어진 특성을 구현합니다. 같은 방식으로 선택적 move 모드가 있습니다. 어쨌든 if foo() { (some future) } else { (other future) } 를 표현하는 다른 방법은 object Future { fn poll() { if foo() { (some future).poll() } else { (other future).poll() } } } 일 것입니다. 결과 올립니다 foo() 아웃로를 let 단지 다른

@glaebhoerl 매우 흥미로운 아이디어입니다! 여기에 Java의 일부 선행 기술도 있습니다.

내 머리 꼭대기에서 몇 가지 생각 (별로 구운 것이 아님) :

  1. [bikeshed] 접두사 object 는 이것이 단지 실존적인 것이 아니라 특성 객체임을 암시하지만, 그렇지 않습니다.

가능한 대체 구문:

impl Future { fn poll() { if foo() { a.poll() } else { b.poll() } } }
// ^ --
// this conflicts with inherent impls for types, so you have to delay
// things until you know whether `Future` is a type or a trait.
// This might be __very__ problematic.

// and perhaps (but probably not...):
dyn Future { fn poll() { if foo() { a.poll() } else { b.poll() } } }
  1. [매크로/설탕] 다음을 얻을 수 있도록 간단한 구문 설탕을 제공할 수 있습니다.
future!(if foo() { a.poll() } else { b.poll() })

예, 구문 질문은 struct 리터럴, 클로저 또는 impl 블록에서 영감을 얻고 싶은지 확실하지 않기 때문에 엉망입니다 enum 가 여기에 [하지만 우리도 추가해야 하지만] 여기에 red herring이라고 생각한다는 것입니다.)

impl Trait 뒤에 있는 if의 다른 분기에서 다른 유형을 반환할 수 있다는 것은 후자에 더 가깝습니다. 컴파일러는 암시적으로 다른 분기를 보유할 유형을 생성하고 요청한 특성을 구현하여 적절한 것으로 디스패치합니다. 그리고 프로그래머는 그 유형이 무엇인지 기록하거나 보지 않으며 참조할 수도 없고 원하는 실제 이유도 없습니다.

흠. 그래서 열거형 유형에 대해 "신선한 이름"을 생성하는 대신 다음과 같은 impls에 해당하는 | 유형을 활용한다고 가정했습니다.

impl<A: IntoIterator, B: IntoIterator> IntoIterator for (A|B)  { /* dispatch appropriately */ }

여러 기능이 동일한 impls를 생성한다는 점에서 분명히 이것에 일관성 문제가 있을 것입니다. 그러나 그것들을 제쳐두고라도, 나는 이 아이디어가 다른 이유로 작동하지 않을 수도 있다는 것을 깨달았습니다. 예를 들어, 연관된 유형이 여러 개 있는 경우 일부 컨텍스트에서는 동일해야 하지만 다른 컨텍스트에서는 다르게 허용될 수 있습니다. 예를 들어 다음을 반환할 수 있습니다.

-> impl IntoIterator<Item = Y>

하지만 우리는 다른 곳에서

-> impl IntoIterator<IntoIter = X, Item = Y>

이것들은 "통합"될 수 없는 두 개의 겹치는 impls가 될 것입니다. 글쎄, 아마도 전문화와 함께.

어쨌든, "비밀 열거형"의 개념이 제 생각에는 더 깨끗해 보입니다.

실존적 임프 특성에 대한 마지막 관심사를 등록하고 싶습니다. impl 특성을 사용하려는 경우의 상당 부분에서 실제로는 둘 이상의 유형을 반환하고 싶습니다.

@nikomatsakis : 반환되는 내용이 경우 가까이에있다라고하는 것이 공평 dyn Trait 보다 impl Trait 때문에 합성 / 익명의 반환 값을 구현 뭔가 동적 파견에 가깝다는?

cc https://github.com/rust-lang/rust/issues/49288 , 최근에 Future s 및 Future -returning trait 메서드로 작업하면서 많이 부딪힌 문제입니다.

FCP가 종료되기 전 마지막 기회이기 때문에 자동 자동 특성에 대한 마지막 주장을 하고 싶습니다. 이것이 마지막 순간이라는 것을 알고 있으므로 기껏해야 현재 구현을 커밋하기 전에 이 문제를 공식적으로 해결하고 싶습니다.

impl Trait 을(를) 팔로우하지 않은 사람을 위해 명확히 하기 위해 이것이 제가 제시하는 문제입니다. impl X 유형으로 표현되는 유형은 현재 자동 특성을 자동으로 구현하며 그 뒤에 있는 구체적인 유형이 해당 자동 특성을 구현하는 경우에만 해당합니다. 구체적으로, 다음 코드가 변경되면 함수는 계속 컴파일되지만 반환하는 형식이 Send 구현한다는 사실에 의존하는 함수의 사용은 실패합니다.

 fn does_some_operation() -> impl Future<Item=(), Error=()> {
-    let data_stored = Arc::new("hello");
+    let data_stored = Rc::new("hello");

     return some_long_operation.and_then(|other_stuff| {
         do_other_calculation_with(data_stored)
     });
}

(간단한 예: 작동 중 , 내부 변경으로 인해 실패 )

이 문제는 명확하지 않습니다. 자동 특성을 "누출"시키려는 매우 고의적인 결정이 있었습니다. 그렇게 하지 않으면 전송되지 않거나 동기화되지 않은 것을 반환하는 모든 함수에 + !Send + !Sync 를 넣어야 합니다. 함수가 반환하는 구체적인 유형에서 단순히 구현할 수 없는 잠재적인 다른 사용자 정의 자동 특성에 대한 불명확한 이야기가 있습니다. 이 두 가지 문제는 나중에 다루겠습니다.

먼저 이 문제에 대한 반대 의견을 간단히 말씀드리고 싶습니다. 이렇게 하면 함수 본문을 변경하여 공개 API를 변경할 수 있습니다. 이것은 코드의 유지보수성을 직접적으로 감소시킵니다.

녹이 개발되는 동안 유용성보다 장황한 측면에서 잘못된 결정이 내려졌습니다. 새로 온 사람들이 이것을 볼 때 그들은 그것이 장황함을 위한 장황한 것이라고 생각하지만 이것은 사실이 아닙니다. 구조가 자동으로 Copy를 구현하지 않도록 할지 또는 함수 서명에서 모든 유형을 명시적으로 만들지 여부에 관계없이 각 결정은 유지 관리를 위한 것입니다.

사람들에게 Rust를 소개하면 확실히 속도, 생산성, 메모리 안전성을 보여줄 수 있습니다. 그러나 이동에는 속도가 있습니다. Ada는 메모리 안전성이 있습니다. 파이썬에는 생산성이 있습니다. Rust가 이 모든 것을 능가하는 것은 유지 관리가 용이합니다. 라이브러리 작성자가 알고리즘을 보다 효율적으로 변경하려는 경우 또는 크레이트 구조를 다시 실행하려는 경우 컴파일러로부터 실수를 했을 때 알려주는 강력한 보증을 받습니다. 내 코드가 메모리 안전뿐만 아니라 논리 및 인터페이스 측면에서도 계속 작동할 것이라고 확신할 수 있습니다. _Rust의 모든 함수 인터페이스는 함수의 유형 선언으로 완전히 표현할 수 있습니다_.

impl Trait 그대로 안정화하는 것은 이러한 믿음에 어긋날 가능성이 큽니다. 물론, 코드를 빠르게 작성하는 것은 매우 좋지만 프로토타입을 만들고 싶다면 파이썬을 사용할 것입니다. Rust는 단기 쓰기 전용 코드가 아닌 장기 유지 관리가 필요할 때 선택하는 언어입니다.


다시 말하지만, 문제가 명확하지 않기 때문에 이것이 나쁠 가능성이 크다고 말합니다. 우선 '자동 특성'에 대한 전체 아이디어는 명확하지 않습니다. Send 및 Sync는 공개 선언이 아닌 구조체의 내용을 기반으로 구현됩니다. 이 결정은 녹슬지 않았으므로 impl Trait 도 비슷하게 작동할 수 있습니다.

그러나 함수와 구조는 코드베이스에서 다르게 사용되며 동일한 문제가 아닙니다.

구조의 필드를 수정할 때, 심지어 private 필드라도 실제 내용을 변경하고 있음을 즉시 알 수 있습니다. non-Send 또는 non-Sync 필드가 있는 구조가 그 선택을 했으며 라이브러리 유지 관리자는 PR이 구조의 필드를 변경할 때 다시 확인하는 것을 알고 있습니다.

함수의 내부를 수정할 때 성능과 정확성 모두에 영향을 미칠 수 있다는 것은 분명합니다. 그러나 Rust에서는 올바른 유형을 반환하는지 확인할 필요가 없습니다. 함수 선언은 우리가 지켜야 하는 엄격한 계약이며 rustc 는 우리를 지켜줍니다. 그것은 구조체의 자동 특성과 함수 반환 사이의 가는 선이지만, 함수의 내부를 변경하는 것은 훨씬 더 일상적입니다. 완전한 발전기 구동 Future 가 있으면 -> impl Future 반환하는 함수를 수정하는 것이 훨씬 더 일상적일 것입니다. 컴파일러가 이를 포착하지 못하는 경우 작성자가 수정된 전송/동기화 구현을 선별해야 하는 모든 변경 사항입니다.

이 문제를 해결하기 위해 원래 RFC 토론 에서 보수적인 impl 특성 RFC의 이 섹션은 자동 특성 누출에 대한 가장 큰 주장을 제시합니다("OIBIT"는 auto 특성의 이전 이름임).

나는 이미 이에 대한 나의 주요 답변을 제시했지만, 여기에 마지막 메모가 있습니다. 구조의 레이아웃을 변경하는 것은 그리 일반적이지 않습니다. 방지할 수 있습니다. 기능이 훨씬 더 많이 변경되기 때문에 기능이 동일한 자동 특성을 계속 구현하도록 하는 유지 관리 부담이 구조의 부담보다 큽니다.


마지막으로, 자동 자동 특성이 유일한 옵션은 아니라는 점을 말씀드리고 싶습니다. 우리가 선택한 옵션이지만 옵트 아웃 자동 특성의 대안은 여전히 ​​대안입니다.

+ !Send + !Sync 상태로 보내기/비동기화 항목을 반환하거나 해당 경계가 있는 특성(별칭?)을 반환하는 함수가 필요할 수 있습니다. 이것은 좋은 결정은 아니지만 현재 선택하는 것보다 나을 수 있습니다.

사용자 정의 자동 특성에 대한 우려에 관해서는 자동 특성 이후에 도입된 새로운 유형에 대해서만 새로운 자동 특성을 구현하지 않아야 한다고 주장합니다. 이것은 내가 지금 해결할 수 있는 것보다 더 많은 문제를 제공할 수 있지만 더 많은 디자인으로 해결할 수 없는 문제는 아닙니다.


이것은 매우 늦고 매우 오랜 시간이 걸렸습니다. 저는 이전에 이러한 반대 의견을 제기한 적이 있다고 확신합니다. 마지막으로 한 번만 논평할 수 있게 되어 기쁩니다. 그리고 우리가 내리는 결정에 전적으로 동의합니다.

읽어주셔서 감사합니다. 최종 결정이 Rust가 나아갈 수 있는 최선의 방향이 되기를 바랍니다.

@daboross 의 리뷰 wrt에서 확장. 특성 별칭을 사용하면 다음과 같이 누출되지 않는 자동 특성의 인체 공학을 개선할 수 있습니다.

trait FutureNSS<T, E> = Future<Item = T, Error= E> + !Send + !Sync;

fn does_some_operation() -> impl FutureNSS<(), ()> {
     let data_stored = Rc::new("hello");
     some_long_operation.and_then(|other_stuff| {
         do_other_calculation_with(data_stored)
     });
}

이것은 그리 나쁘지 않습니다. 좋은 이름을 지어야 합니다( FutureNSS 는 그렇지 않습니다). 주요 이점은 경계 반복으로 인해 발생하는 용지 절단을 줄일 수 있다는 것입니다.

auto-traits를 명시적으로 명시해야 하는 요구 사항으로 이 기능을 안정화하고 나중에 해당 유지 관리 문제에 대한 적절한 솔루션을 찾거나 실제로 유지 관리 부담이 없다고 확신하면 이러한 요구 사항을 제거할 수 있지 않을까요? 요구 사항을 해제하기로 결정?

!Send 로 표시되지 않는 한 Send 요구하지만 동기화로 표시되지 않는 한 Sync 하지 않는 것은 어떻습니까? Send는 Sync에 비해 더 일반적이어야 하지 않습니까?

이와 같이:

fn provides_send_only1() -> impl Trait {  compatible_with_Send_and_Sync }
fn provides_send_only2() -> impl Trait {  compatible_with_Send_only }
fn fails_to_complile1() -> impl Trait {  not_compatible_with_Send }
fn provides_nothing1() -> !Send + impl Trait { compatible_with_Send}
fn provides_nothing2() -> !Send + impl Trait { not_compatible_with_Send }
fn provides_send_and_sync() -> Sync + impl Trait {  compatible_with_Send_and_Sync }
fn fails_to_compile2() -> Sync + impl Trait { compatible_with_Send_only }

인수 위치의 impl Trait 와 반환 위치 wrt 사이에 불일치가 있습니까? 자동차 특성?

fn foo(x: impl ImportantTrait) {
    // Can't use Send cause we have not required it...
}

여기로 보내기를 가정할 수 있는 경우 인수 위치 원인에 대해 의미가 있습니다. 단형화 후 오류가 발생합니다. 물론 여기에서 반환 위치와 인수 위치에 대한 규칙이 일치할 필요는 없지만 학습성 측면에서 문제가 있습니다.

사용자 정의 자동 특성에 대한 우려에 관해서는 자동 특성 이후에 도입된 새로운 유형에 대해서만 새로운 자동 특성을 구현하지 않아야 한다고 주장합니다.

음, 이것은 마찬가지입니다 곧 자동 특징 부 (trait) Unpin 엄청난 행운을 것 같다 ... (단지 자기 참조 발전기 - implmented되지 않음),하지만? 이것이 우리가 정말로 살 수 있는 한계입니까? &mut 또는 Rc 와 같이 앞으로 비활성화해야 하는 일이 없을 것이라고는 믿을 수 없습니다.

나는 이것이 논의되었다고 생각하며 이것은 물론 매우 늦었지만 여전히 impl Trait 인수 위치에 만족하지 않습니다.

값에 의해 폐쇄 / 미래와 모두 a) 작업에 능력, 및 b)는 "출력"함으로써 구현 정보로서 어떤 종류의 치료 관용적 그리고 그들이 직접 성능, 안정성 녹 핵심 값을 지원하기 때문에, 1.0 이전부터 있었다 그리고 안전.

-> impl Trait 는 따라서 1.0에서 한 약속을 이행하거나, 엣지 케이스를 제거하거나, 기존 기능을 일반화하는 것뿐입니다. 즉, 출력 유형을 함수에 추가하고, 항상 익명 유형을 처리하고 적용하는 데 사용되는 동일한 메커니즘을 사용합니다. 더 많은 경우에. abstract type , 즉 모듈의 출력 유형으로 시작하는 것이 더 원칙적일 수 있지만 Rust에 ML 모듈 시스템이 없다는 점을 감안하면 순서는 큰 문제가 아닙니다.

fn f(t: impl Trait) 대신 "우리가 할 수 있기 때문에" 추가된 것처럼 느껴져 충분한 보답 없이 언어를 더 크고 이상하게 만듭니다. 나는 어려움을 겪었고 그것에 맞는 기존 프레임 워크를 찾지 못했습니다. 나는의 간결의 주위에 인수 이해 fn f(f: impl Fn(...) -> ...) , 그리고 경계가 이미 모두 할 수있는 정당성 <T: Trait>where 조항하지만 중공 그 느낌을. 그들은 단점을 부정하지 않습니다:

  • 이제 경계에 대한 두 가지 구문을 배워야 합니다. 최소 <> / where 는 단일 구문을 공유합니다.

    • 이것은 또한 학습 절벽을 만들고 여러 장소에서 동일한 제네릭 유형을 사용한다는 아이디어를 모호하게 만듭니다.

    • 새 구문을 사용하면 전체 인수 목록을 스캔해야 하는 대신 함수가 무엇인지 말하기가 더 어려워집니다.

  • 당신이 유형을 쓸 수 없기 때문에 이제 어떻게 함수의 구현 세부 사항, (그것의 형식 매개 변수를 선언하는 방법)의 인터페이스의 일부가되어야합니다!

    • 이것은 또한 현재 논의 중인 자동 특성 문제와도 관련이 있습니다.

  • dyn Trait 의 유추는 솔직히 거짓입니다.

    • dyn Trait 항상 같은 것을 의미하며 기존 자동 특성 메커니즘을 통하지 않고는 주변 선언을 "감염"시키지 않습니다.

    • dyn Trait 는 데이터 구조에서 사용할 수 있으며 이는 실제로 주요 사용 사례 중 하나입니다. 데이터 구조에서 impl Trait 는 데이터 구조의 모든 용도를 살펴보지 않고는 의미가 없습니다.

    • dyn Trait 의미하는 것 중 일부는 유형 삭제이지만 impl Trait 는 구현에 대해 아무 것도 의미하지 않습니다.

    • 단형화되지 않은 제네릭을 도입하면 앞의 요점은 훨씬 더 혼란스러울 것입니다. 실제로 이러한 상황에서 fn f(t: impl Trait) 는 a) 새 기능과 함께 작동하지 않거나 b) 자동 특성 문제와 같은 더 많은 경우에 변호사가 필요합니다. fn f<dyn T: Trait>(t: T, u: dyn impl Urait) 상상해보세요! :비명:

그래서 결론은 인수 위치에 impl Trait 가 추가되고, 이상한 예산을 더 많이 사용하고, 언어가 더 크게 느껴지도록 하는 것입니다. 반면 반환 위치에 있는 impl Trait 는 통합되고, 단순화하고 언어를 더 밀접하게 연결합니다.

!Send로 표시되어 있지 않으면 Send를 요구하지만 Sync로 표시되어 있지 않으면 Sync를 제공하지 않는 것은 어떻습니까? Send는 Sync에 비해 더 일반적이어야 하지 않습니까?

그것은 매우... 임의적이고 임시적인 느낌입니다. 아마도 타이핑은 덜하지만 더 기억하고 더 혼란스러울 것입니다.

위의 내 요점에서 산만하지 않도록 여기에서 자전거 보관소 아이디어: impl 대신 type ? 이것은 연관된 유형에 사용되는 키워드이며 abstract type 사용되는 키워드일 가능성이 높으며 여전히 상당히 자연스럽고 "함수의 출력 유형"에 대한 아이디어를 더 많이 암시합니다.

// keeping the same basic structure, just replacing the keyword:
fn f() -> type Trait

// trying to lean further into the concept:
fn f() -> type R: Trait
fn f() -> type R where R: Trait
fn f() -> (i32, type R) where R: Trait
// or perhaps:
fn f() -> type R: Trait in R
// or maybe just:
fn f() -> type: Trait

읽어주셔서 감사합니다. 최종 결정이 Rust가 나아갈 수 있는 최선의 방향이 되기를 바랍니다.

잘 작성된 이의 제기에 감사드립니다. 지적했듯이 자동 특성은 항상 숨겨져 있을 것으로 예상되는 일부 구현 세부 정보를 "노출"하기 위한 의도적인 선택이었습니다. 제 생각에는 -- 지금까지는 -- 그 선택이 실제로 꽤 잘 이루어졌다고 생각합니다.

중요한 질문이 기능은 정말 구조체는 다른있는 정도라고 나에게 보인다

구조의 레이아웃을 변경하는 것은 그리 일반적이지 않습니다. 방지할 수 있습니다. 기능이 훨씬 더 많이 변경되기 때문에 기능이 동일한 자동 특성을 계속 구현하도록 하는 유지 관리 부담이 구조의 부담보다 큽니다.

이것이 얼마나 사실인지 알기는 정말 어렵습니다. Rc 를 도입하는 것은 주의해야 하는 것이 일반적인 규칙인 것 같습니다. 저장 위치에 대한 문제는 그다지 중요하지 않습니다. (사실, 제가 실제로 작업하는 경우는 Rc 아니라 dyn Trait 소개하는 것입니다. 그 이유는 덜 명확할 수 있기 때문입니다.)

나는 미래를 반환하는 코드에서 스레드로부터 안전하지 않은 유형 등으로 작업하는 경우가 드물다고 강력히 의심합니다. 당신은 그런 종류의 라이브러리를 피하는 경향이 있을 것입니다. (물론 실제 시나리오에서 테스트를 수행하는 것은 항상 비용이 듭니다.)

어쨌든 안정화 기간을 아무리 길게 주어도 미리 알기 힘든 부분이라 답답합니다.

마지막으로, 자동 자동 특성이 유일한 옵션은 아니라는 점을 말씀드리고 싶습니다. 우리가 선택한 옵션이지만 옵트 아웃 자동 특성의 대안은 여전히 ​​대안입니다.

사실, Send 와 같은 특정 자동차 특성을 "선택"한다는 아이디어에 대해 확실히 긴장하고 있습니다. 또한 future 외에도 impl 특성에 대한 다른 사용 사례가 있음을 염두에 두고 있습니다. 예를 들어 반복자 또는 클로저를 반환하는 경우 -- 기본적으로 보내기 또는 동기화를 원하는지 명확하지 않습니다. 어쨌든, 당신이 정말로 원하고 우리가 연기하려고 하는 것은 일종의 "조건부" 바운드입니다( T 가 Send인 경우 Send). 이것이 바로 자동 특성이 제공하는 것입니다.

@rpjohnst

나는 이것이 논의되었다고 믿는다

실제로, 이것은 몇 년 전 impl Trait RFC 이후로 계속 그렇습니다. (워, 2014. 늙어보이네.)

나는 어려움을 겪었고 그것에 맞는 기존 프레임 워크를 찾지 못했습니다.

나는 정반대라고 느낀다. 나에게 impl Trait 가 인수 위치에 없으면 반환 위치에 impl Trait 가 더 두드러집니다. 내가 보는 통합 스레드는 다음과 같습니다.

  • impl Trait -- 나타나는 곳에 " Trait 를 구현하는 일부 단형성 유형"이 있음을 나타냅니다. (호출자 또는 수신자 중 누가 해당 유형을 지정하는지에 대한 질문은 impl Trait 가 표시되는 위치에 따라 다릅니다.)
  • dyn Trait -- 표시되는 곳에 Trait 를 구현하는 일부 유형이 있을 것이지만 유형 선택은 동적으로 이루어짐을 나타냅니다.

그 직관을 바탕으로 impl Trait 이 나타날 수 있는 장소를 확장할 계획도 있습니다. 예를 들어 https://github.com/rust-lang/rfcs/pull/2071 허용

let x: impl Trait = ...;

동일한 원칙이 적용됩니다. 유형 선택은 정적으로 알려져 있습니다. 마찬가지로 동일한 RFC는 abstract type ( impl Trait 는 일종의 구문 설탕으로 이해될 수 있음)를 도입했으며 이는 특성 impls 및 모듈의 구성원으로도 나타날 수 있습니다.

위의 내 요점에서 산만하지 않도록 여기에서 자전거 보관소 아이디어: impl 대신 type ?

개인적으로 나는 여기에서 자전거 보관소를 다시 활성화하고 싶지 않습니다. 우리는 https://github.com/rust-lang/rfcs/pull/2071 및 다른 곳에서 구문에 대해 논의하는 데 상당한 시간을 보냈습니다. "완벽한 키워드"가 없는 것 같지만 impl 를 "구현하는 일부 유형"으로 읽는 것은 꽤 잘 작동합니다.

자동 특성 누출에 대해 조금 더 추가하겠습니다.

우선, 궁극적으로 자동 특성 누출이 실제로 여기에서 하는 것이 옳다고 생각합니다. 정확히는 그것이 나머지 언어와 일치하기 때문입니다. 자동 특성은 앞서 말했듯이 항상 도박이었지만 기본적으로 성과를 거둔 것 같습니다. impl Trait 이(가) 그렇게 많이 다르지 않다고 생각합니다.

그러나 또한 여기에서 지연되는 것에 대해 꽤 긴장하고 있습니다. 내가 거기 설계 공간에서 다른 흥미로운 점은 나는 우리가 정확한 지점에 충돌 한 100 % 확신하지 않다 동의하지만, 나는 우리가 이제까지 확인이 될 것입니다 모르겠어요. 지금 연기하면 올해 로드맵을 전달하는 데 어려움을 겪지 않을까 걱정이 됩니다.

마지막으로 내가 틀렸을 때의 의미를 생각해 봅시다. 여기서 우리가 기본적으로 이야기하는 것은 semver가 판단하기 훨씬 더 미묘해진다는 것입니다. 이것은 우려 사항이지만 다양한 방법으로 완화할 수 있는 문제라고 생각합니다. 예를 들어 !Send 또는 !Sync 유형이 도입될 때 경고하는 린트를 사용할 수 있습니다. 우리는 우발적인 semver 위반을 방지하는 데 도움이 되는 semver 검사기를 도입하는 것에 대해 오랫동안 이야기해 왔습니다. 이것은 도움이 될 또 다른 경우인 것 같습니다. 요컨대 문제이지만 중요한 문제는 아니라고 생각합니다.

그래서 -- 적어도 이 순간만큼은 -- 나는 여전히 현재의 길을 계속 가고 싶은 마음이 있습니다.

개인적으로 나는 여기에서 자전거 보관소를 다시 활성화하고 싶지 않습니다.

나도 그것에 대해 크게 투자하지 않습니다. impl Trait 인수 위치의 의미 상이 아니라

나에게 impl Trait 가 인수 위치에 없으면 반환 위치에 impl Trait 가 더 두드러집니다.

연관된 유형에 대한 비유를 감안할 때 이는 "인수 위치에 type T 가 없으면 연관된 유형이 훨씬 더 두드러집니다."와 매우 유사합니다. 우리가 선택한 구문이 무의미하게 느껴지기 때문에 특정 이의가 제기되지 않은 것 같습니다. 기존 구문이 trait Trait<type SomeAssociatedType> 와 같은 구문 설탕의 필요성을 느끼지 않을 만큼 충분히 좋습니다.

우리는 이미 " Trait 를 구현하는 일부 단형성 유형"에 대한 구문을 가지고 있습니다 . 특성의 경우 "호출자" 및 "피호출자" 지정 변형이 모두 있습니다. 함수의 경우 호출자 지정 변형만 있으므로 호출 수신자 지정 변형에 대한 새 구문이 필요합니다.

이 새로운 구문을 지역 변수로 확장하는 것은 정당화될 수 있습니다. 이는 또한 매우 연관된 유형과 유사한 상황이기 때문입니다. 이는 표현식 의 출력 유형을 숨기고 이름을 지정하는 방법이며 호출 수신자 함수의 출력 유형을 전달하는 데 유용합니다.

이전 댓글에서 언급했듯이 저도 abstract type 의 팬입니다. 다시 말하지만, 단순히 "출력 유형" 개념을 모듈로 확장한 것입니다. 그리고 -> impl Trait , let x: impl Traitabstract type 의 추론을 특성 impls의 관련 유형에 적용하는 것도 훌륭합니다.

특히 내가 싫어하는 함수 인수에 대해 이 새로운 구문을 추가하는 개념입니다. 가져오는 다른 기능과 동일한 작업을 수행 하지 않습니다 . 그것은 단지 더 에지의 경우 적은 적용, 우리가 이미 가지고있는 구문과 같은 일을한다. :/

@nikomatsakis

이것이 얼마나 사실인지 알기는 정말 어렵습니다.

그렇다면 우리가 보수적이라는 면에서 오류를 범해야 하는 것 같습니까? 시간이 지나면서 디자인에 대한 확신을 더 많이 얻을 수 있습니까(자동 특성 누출이 별도의 기능 게이트 아래에 있고 나머지 impl Trait 안정화하는 동안 야간에만)? 지금 누출하지 않으면 나중에 자동 특성 누출에 대한 지원을 항상 추가할 수 있습니다.

그러나 또한 여기에서 지연되는 것에 대해 꽤 긴장하고 있습니다. [..] 지금 미루면 올해 로드맵을 전달하는 데 어려움을 겪지 않을까 걱정이 됩니다.

이해할 수 있는! 그러나, 그리고 귀하가 고려했을 것이라고 확신합니다. 여기에서의 결정은 앞으로 몇 년 동안 우리와 함께 할 것입니다.

예를 들어 !Send 또는 !Sync 유형이 도입될 때 경고하는 린트를 사용할 수 있습니다. 우리는 우발적인 semver 위반을 방지하는 데 도움이 되는 semver 검사기를 도입하는 것에 대해 오랫동안 이야기해 왔습니다. 이것은 도움이 될 또 다른 경우인 것 같습니다. 요컨대 문제이지만 중요한 문제는 아니라고 생각합니다.

이것은 듣기 좋은 것입니다! 🎉 그리고 이것이 제 걱정을 대부분 덜어준다고 생각합니다.

사실, Send 와 같은 특정 자동차 특성을 "선택"한다는 아이디어에 대해 확실히 긴장하고 있습니다.

👍 이분말에 아주 공감합니다.

어쨌든, 당신이 정말로 원하고 우리가 연기하려고 하는 것은 일종의 "조건부" 바운드입니다( T 가 Send인 경우 Send). 이것이 바로 자동 특성이 제공하는 것입니다.

코드에 명시적으로 명시되어 있다면 T: Send => Foo<T>: Send 가 더 잘 이해될 것이라고 생각합니다.

fn foo<T: Extra, trait Extra = Send>(x: T) -> impl Bar + Extra {..}

Tho, WG-Traiits에서 논의한 것처럼 여기에서는 추론을 전혀 얻지 못할 수 있으므로 Send 이외의 것을 원하는 경우 항상 Extra 를 지정해야 합니다. .

@rpjohnst

dyn Trait 의 유추는 솔직히 거짓입니다.

인수 위치의 impl Trait 에 대해서는 false이지만 -> impl Trait 는 둘 다 존재 유형이므로 그렇지 않습니다.

  • 이제 함수의 구현 세부 사항(유형 매개변수 선언 방법)이 인터페이스의 일부가 되어야 합니다. 유형을 작성할 수 없기 때문입니다!

유형 매개변수의 순서는 Turbofish로 인해 구현 세부 사항이 된 적이 없으며 이와 관련하여 Turbofish에서 특정 유형 인수를 지정하지 않은 상태로 둘 수 있기 때문에 impl Trait 가 도움이 될 수 있다고 생각합니다. .

[..] 기존 구문은 특성 특성과 같은 구문 설탕의 필요성을 아무도 느끼지 않을 만큼 충분히 좋습니다..

절대 말 안해? https://github.com/rust-lang/rfcs/issues/2274

@nikomatsakis 처럼, 저는 이 마지막 순간의 댓글에 세심한 주의를 기울였습니다. 특히 이 기능만큼 오랫동안 원했던 기능을 위해 화물 열차 앞에 몸을 던지는 것과 같은 느낌이 들 수 있다는 것을 압니다!


@daboross , 나는 옵트아웃 아이디어를 조금 더 파고 싶었습니다. 처음에는 얼굴을 붉히며 유망해 보입니다. 왜냐하면 서명을 완전히 명시할 수 있기 때문입니다. 그러나 기본값은 일반적인 경우를 간결하게 만듭니다.

하지만 불행히도 큰 그림을 보기 시작하면 몇 가지 문제가 발생합니다.

  • 자동 특성이 impl Trait 대한 옵트아웃으로 처리된 경우 dyn Trait 에도 적용되어야 합니다.
  • 이것은 물론 이러한 구문이 인수 위치에서 사용되는 경우에도 적용됩니다.
  • 그러나 제네릭이 다르게 동작하는 것은 다소 이상할 것입니다. 즉, fn foo<T>(t: T) 경우 기본적으로 T: Send 를 합리적으로 기대할 수 있습니다.
  • 물론 현재 Sized 에만 적용되는 이에 대한 메커니즘이 있습니다. 모든 곳에서 기본적으로 가정되는 특성이며 ?Sized 작성하여 선택 해제합니다.

?Sized 메커니즘은 Rust의 가장 모호하고 가르치기 어려운 측면 중 하나로 남아 있으며, 일반적으로 우리는 이를 다른 개념으로 확장하는 것을 극도로 싫어합니다. Send 와 같은 핵심적인 개념으로 사용하는 것은 위험해 보입니다. 물론 큰 변화가 될 것이라는 점은 말할 것도 없습니다.

더군다나 우리는 제네릭에 대한 자동 특성 가정을 적용하고 싶지 않습니다 . 왜냐하면 오늘날 제네릭의 장점 중 하나는 유형이 자동 특성을 구현하는지 여부에 대해 효과적으로 제네릭 될 수 있고 해당 정보가 "흐르다". 예를 들어 fn f<T>(t: T) -> Option<T> . 우리 전달할 수 T 관계없이인지 Send 하고, 출력 될 것이다 Send IFF T 이었다. 이것은 Rust의 제네릭 이야기에서 매우 중요한 부분입니다.

dyn Trait 에도 문제가 있습니다. 특히, 별도의 컴파일 때문에 이 "선택 해제" 특성을 SendSync 와 같은 "잘 알려진" 자동 특성으로만 제한해야 합니다. 그것은 아마도 외부 사용을 위해 auto trait 를 안정화하지 않는다는 것을 의미할 것입니다.

마지막으로 "누출" 디자인은 불투명한 유형을 반환하기 위해 newtype 래퍼를 만들 때 오늘날 발생하는 일을 따라 명시적으로 모델링되었음을 반복할 가치가 있습니다. 기본적으로 저는 "누설"이 자동차 특성의 본질적인 측면이라고 믿습니다. 장단점이 있지만 핵심은 기능이며 그에 따라 상호 작용할 수 있는 새로운 기능을 위해 노력해야 한다고 생각합니다.


@rpjohnst

위의 RFC 및 @nikomatsakis 의 요약 설명에 대한 광범위한 토론 후에 인수 위치 문제에 대해 추가할 내용이 많지 않습니다.

이제 함수의 구현 세부 사항(유형 매개변수 선언 방법)이 인터페이스의 일부가 되어야 합니다. 유형을 작성할 수 없기 때문입니다!

이게 무슨 말인지 이해가 안가네요. 확장할 수 있습니까?

또한 다음과 같은 문구에 주목하고 싶습니다.

fn f(t: impl Trait) 대신 "우리가 할 수 있기 때문에" 추가된 것처럼 느껴집니다.

선의의 토론을 훼손합니다(반복되는 패턴이기 때문에 이것을 부르는 것입니다). RFC는 기능에 동기를 부여하고 여기에서 주장하는 일부 주장을 반박하기 위해 상당한 시간 을 들입니다. 물론 스레드에 대한 논의는 말할 것도 없고, RFC의 이전 반복 등에서도 마찬가지입니다.

절충점이 존재하고 실제로 단점이 있지만 토론의 "상대방"을 희화화하기 위한 합리적인 결론에 도달하는 데 도움이 되지 않습니다.

자세한 의견을 주신 모든 분들께 감사드립니다! 마침내 impl Trait 를 안정적인 버전으로 출시하게 되어 정말 기쁩니다. 그래서 저는 현재 구현과 그에 따른 디자인 결정에 크게 치우쳐 있습니다. 즉, 최대한 공정하게 응답하고 0부터 시작하는 것처럼 고려하도록 최선을 다할 것입니다.

auto Trait 누출

auto Trait 누출에 대한 아이디어는 오랫동안 저를 괴롭혔습니다. 어떤 면에서 이것은 Rust의 많은 디자인 목표와 상반되는 것처럼 보일 수 있습니다. C++ 또는 ML 제품군과 같은 조상과 비교할 때 Rust는 함수 선언에 명시적으로 명시적으로 지정되어야 하는 일반 범위를 요구한다는 점에서 특이합니다. 제 생각에 이것은 Rust의 일반 함수를 더 쉽게 읽고 이해할 수 있도록 하며, 이전 버전과 호환되지 않는 변경이 이루어질 때 상대적으로 명확해집니다. 우리는 const fn 에 대한 접근 방식에서 이 패턴을 계속 const 성을 추론하는 대신 const 로 명시적으로 지정하는 함수를 요구합니다. 명시적 특성 경계와 매우 유사하게 이는 어떤 기능이 어떤 방식으로 사용 가능한지 쉽게 알 수 있도록 하고 라이브러리 작성자에게 작은 구현 변경으로 인해 사용자가 중단되지 않을 것이라는 확신을 줍니다.

즉, 나는 Fuchsia 운영 체제에 대한 작업을 포함하여 내 자신의 프로젝트에서 return-position impl Trait 광범위하게 사용했으며 자동 특성 누출이 여기에서 올바른 기본값이라고 믿습니다. 실제로, 누수를 제거한 결과는 내가 작성한 모든 impl Trait 에 기본적으로 + Send 를 추가해야 한다는 것입니다. 음수 범위( + !Send )는 흥미로운 아이디어지만, 그러면 거의 모든 동일한 기능에 + !Unpin 를 쓸 것입니다. 명시성은 사용자의 결정을 알리거나 코드를 더 이해하기 쉽게 만들 때 유용합니다. 이 경우에는 둘 다 하지 않을 것이라고 생각합니다.

SendSync 는 사용자가 프로그래밍하는 "컨텍스트"입니다. Send!Send 유형을 모두 사용하는 응용 프로그램이나 라이브러리를 작성하는 경우는 극히 드뭅니다. (특히 멀티스레드이든 아니든 중앙 실행기에서 실행할 비동기 코드를 작성할 때). 스레드로부터 안전한지 아닌지에 대한 선택은 애플리케이션을 작성할 때 가장 먼저 선택해야 하는 것 중 하나이며, 그때부터 스레드로부터 안전한지 선택하는 것은 모든 유형이 Send 여야 한다는 것을 의미합니다. 라이브러리의 경우 거의 항상 내가 Send 유형을 선호하는 경우입니다. 이를 사용하지 않는다는 것은 스레드 컨텍스트 내에서 사용할 때 라이브러리를 사용할 수 없다는 것을 의미합니다(또는 전용 스레드를 생성해야 함). 경쟁이 없는 parking_lot::Mutex 는 최신 CPU에서 사용할 때 RefCell 거의 동일한 성능을 가지므로 사용자가 !Send 용 라이브러리 기능을 전문화하도록 유도할 동기가 없습니다. 케이스. 이러한 이유로 함수 서명 수준에서 Send!Send 유형을 구별하는 것이 중요하지 않다고 생각합니다. 라이브러리 작성자는 실수로 !Send 유형을 이전에 Send 였던 impl Trait 유형에 도입했습니다. 이 선택이 가독성과 명료성 비용과 함께 제공되는 것은 사실이지만 인체 공학 및 사용 편의성 측면에서 절충안이 충분히 가치가 있다고 생각합니다.

인수 위치 impl Trait

내가 인수 위치 impl Trait 도달할 때마다 함수 시그니처의 가독성과 전반적인 즐거움이 크게 증가한다는 것을 발견했다는 점을 제외하고는 여기서 할 말이 너무 없습니다. 오늘날의 Rust에서는 불가능한 새로운 기능을 추가하지 않은 것은 사실이지만 복잡한 함수 서명에 대한 삶의 질을 크게 향상시켰으며 반환 위치 impl Trait 와 개념적으로 잘 짝을 이루었습니다. OOP 프로그래머가 행복한 Rustaceans로 쉽게 전환할 수 있습니다. 현재, 중복 많이 제공하기 위해 단지라는 이름의 제네릭 형식을 도입 할 필요가 주위에있다 바운드 (예 : F 에서 fn foo<F>(x: F) where F: FnOnce()fn foo(x: impl FnOnce()) ). 이 변경 사항은 해당 문제를 해결하고 읽기 및 쓰기가 더 쉬운 함수 서명을 생성하며 IMO는 -> impl Trait 와 함께 자연스럽게 맞는 것처럼 느껴집니다.

TL; DR: 우리의 원래 결정이 옳았다고 생각하지만 의심할 여지 없이 절충안이 따릅니다.
Rust가 가능한 최고의 언어인지 확인하기 위해 많은 시간과 노력을 기울이고 목소리를 내는 모든 사람에게 정말 감사합니다.

@센트릴

인수 위치의 impl Trait과 관련하여 거짓이지만 -> impl Trait 둘 다 존재 유형이므로 그렇지 않습니다.

그래, 그게 내 말이었다.

@aturon

... 선의의 토론을 훼손하는 것과 같은 문구

당신 말이 맞아요. 나는 내가 다른 곳에서 내 요점을 더 잘 만들었다고 생각합니다.

이제 함수의 구현 세부 사항(유형 매개변수 선언 방법)이 인터페이스의 일부가 되어야 합니다. 유형을 작성할 수 없기 때문입니다!

이게 무슨 말인지 이해가 안가네요. 확장할 수 있습니까?

인수 위치에서 impl Trait 를 지원하면 이 함수를 두 가지 방법으로 작성할 수 있습니다.

fn f(t: impl Trait)
fn f<T: Trait>(t: T)

형식 선택은 API 소비자가 특정 인스턴스의 이름을 기록 할 수 있는지 여부를 결정합니다(예: 주소 가져오기). impl Trait 변형으로는 그렇게 할 수 없으며 <T> 구문을 사용하도록 서명을 다시 작성하지 않고는 항상 해결할 수 없습니다. 또한 <T> 구문으로 이동하는 것은 주요 변경 사항입니다!

캐리커처가 추가될 위험이 있지만 이에 대한 동기는 가르치고 배우고 사용하기가 더 쉽기 때문입니다. 그러나 둘 사이의 선택은 형식 매개변수 순서와 마찬가지로 함수 인터페이스의 주요 부분 이기도 하기 때문에 이것이 적절하게 해결되었다고 생각하지 않습니다. 결과적으로 더 즐거운 기능 서명이 생성됩니다.

학습성/인체 공학에 의해 동기가 부여된 다른 "단순하지만 제한적 -> 복잡하지만 일반적인" 변경 사항에는 이러한 방식으로 인터페이스를 깨는 변경 사항이 포함되어 있는지 잘 모르겠습니다. 간단한 방법의 복잡한 등가물은 동일하게 동작하고 이미 인터페이스 또는 동작을 변경하는 경우에만 전환하면 됩니다(예: 수명 제거, 인체 공학 일치, -> impl Trait ). 보편적으로 적용됩니다(예: 모듈/경로, 대역 내 수명, dyn Trait ).

좀 더 구체적으로 말하자면, 라이브러리에서 이 문제가 발생하기 시작할 것이라고 걱정합니다. "모든 사람이 Copy / Clone 파생을 기억해야 함"과 비슷하지만 더 나쁜 이유는 a) 그것은 주요 변경 사항이 될 것이며, b) 항상 뒤로 물러나야 하는 긴장이 있을 것입니다. 특히 그것이 기능이 설계된 이유이기 때문입니다!

@cramertj 함수 서명 중복이 진행되는 한... 다른 방법으로 제거할 수 있습니까? 대역 내 수명은 역참조 없이 벗어날 수 있었습니다. 아마도 우리는 "대역 내 유형 매개변수"와 같은 도덕적 동등성을 어떻게든 할 수 있을 것입니다. 즉, "변경 사항은 일반적이며 보편적으로 적용되는 것입니다."

@rpjohnst

또한 <T> 구문으로 이동하는 것은 주요 변경 사항입니다!

반드시 그런 것은 아니지만 https://github.com/rust-lang/rfcs/pull/2176 을 사용하면 끝에 추가 유형 매개변수 T: Trait 를 추가할 수 있으며 Turbofish는 계속 작동합니다(다음에 의한 파손을 언급하지 않는 한 터보피쉬 파손 이외의 다른 수단).

impl Trait 변형을 사용하면 그렇게 할 수 없으며 <T> 구문을 사용하도록 서명을 다시 작성하지 않고는 항상 해결할 수 없습니다. 또한 <T> 구문으로 이동하는 것은 주요 변경 사항입니다!

또한 <T> 구문 에서 이동하는 것이 주요 변경 사항임을 의미한다고 생각합니다(호출자가 더 이상 Turbofish를 사용하여 T 값을 명시적으로 지정할 수 없기 때문입니다).

업데이트: 함수가 impl Trait를 사용하는 경우 현재 일반 일반 매개변수가 있는 경우에도 터보피시 사용을 전혀 허용하지 않습니다.

@nikomatsakis 이전 서명에 명시적 유형 매개변수와 암시적 매개변수가 혼합된 경우 명시적 구문으로 이동 하는 것도 큰 변화가 될 수 있습니다. n 유형 매개변수를 제공한 사람은 이제 n + 1 을 제공해야 합니다. 대신 @Centril 의 RFC가 해결하고자 하는 경우 중 하나였습니다.

업데이트: 함수가 impl Trait를 사용하는 경우 현재 일반 일반 매개변수가 있는 경우에도 터보피시 사용을 전혀 허용하지 않습니다.

이것은 기술적으로 중단 사례의 수를 줄이지만 다른 한편으로 특정 인스턴스화의 이름을 지정할 수 없는 경우의 수를 증가시킵니다. :(

@nikomatsakis

이 문제를 진심으로 해결해 주셔서 감사합니다.

나는 여전히 자동 특성 누출이 _올바른_ 해결책이라고 말하기를 주저하지만 사실이 끝날 때까지 무엇이 최선인지 알 수 없다는 데 동의합니다.

저는 주로 Futures 사용 사례를 고려했지만 이것이 유일한 경우는 아닙니다. 로컬 유형에서 Send/Sync를 누출하지 않으면 다양한 컨텍스트에서 impl Trait 를 사용하는 것에 대한 좋은 이야기가 없습니다. 이것을 감안할 때 추가 자동 특성이 주어지면 내 제안은 실제로 실행 가능하지 않습니다.

저는 SyncSend 하고 _only_ 가정하고 싶지 않았습니다. 이는 약간 임의적이며 _one_ 사용 사례에만 가장 적합하기 때문입니다. 그러나 모든 자동 특성을 가정하는 대안도 좋지 않습니다. 모든 유형의 + !Unpin + !... 는 실행 가능한 솔루션처럼 들리지 않습니다.

효과 시스템과 지금은 전혀 알 수 없는 다른 아이디어를 생각해내기 위해 언어 디자인을 5년 더 했다면 더 나은 것을 생각해낼 수 있을지도 모릅니다. 그러나 현재로서는, 그리고 Rust에게는 100% "자동" 자동 특성을 갖는 것이 가장 좋은 방법인 것 같습니다.

@lfairy

이전 서명에 명시적 유형 매개변수와 암시적 매개변수가 혼합된 경우 명시적 구문으로 이동하는 것도 큰 변화가 될 수 있습니다. n 유형 매개변수를 제공한 사람은 이제 n + 1 대신

그것은 현재 허용되지 않습니다. impl Trait 를 사용하면 매개변수에 대해 터보피쉬를 얻을 수 없습니다(내가 언급한 대로). 이것은 장기적인 해결책이 아니라 둥근 디자인을 생각해낼 시간이 있을 때까지 진행 방법에 대한 의견 불일치를 피하기 위한 보수적인 단계입니다. (그리고 @rpjohnst가 언급했듯이 고유한 단점이 있습니다.)

내가 보고 싶은 디자인은 (a) @centril 의 RFC 또는 이와 유사한 것을 수락하고 (b) 명시적 매개변수에 대해 Turbofish를 사용할 수 있다고 말하는 것입니다( impl Trait 유형은 아님). 그러나 부분적 으로 는 명시적 매개변수 에서 impl 특성으로 마이그레이션할 수 있는 스토리가 있는지 궁금해서 그렇게 하지 않았습니다.

@lfairy

그것은 @Centril 의 RFC가 해결하고자 하는 경우 중 하나였습니다.

_[Trivia]_ 덧붙여서, 부분 터보피쉬가 <T: Trait>impl Trait 사이의 간격을 완화할 수 있다는 사실을 제 관심을 끌게 한 것은 실제로

유추, 기본값, 명명된 매개변수 등에 대해 더 많은 확신을 갖게 되면 부분적 터보피시인 결국™도 가질 수 있기를 바랍니다.

이제 최종 의견 수렴 기간이 완료되었습니다.

이것이 1.26으로 배송된다면 https://github.com/rust-lang/rust/issues/49373 이 매우 중요해 보입니다. FutureIterator 는 두 가지 주요 용도입니다. -케이스와 둘 다 연관된 유형을 아는 것에 크게 의존합니다.

문제 추적기에서 빠른 검색을 수행했으며 #47715는 여전히 수정해야 하는 ICE입니다. 안정화되기 전에 이것을 얻을 수 있습니까?

오늘 impl Trait에서 만난 것:
https://play.rust-lang.org/?gist=69bd9ca4d41105f655db5f01ff444496&version=stable

impl Traitunimplemented!() 와 호환되지 않는 것 같습니다. 알려진 문제입니까?

예, #36375 및 #44923을 참조하십시오.

RFC 1951의 가정 2 가 비동기 블록과 함께 계획된 impl Trait 사용 중 일부와 반대 된다는 것을 방금 깨달았습니다. 특히 일반 AsRef 또는 Into 매개변수를 사용하여 보다 인체공학적인 API를 사용하고 async 블록을 반환하기 전에 이를 소유 유형으로 변환하면 여전히 반환됩니다. impl Trait 유형은 해당 매개변수의 수명에 의해 제한됩니다. 예를 들어

impl HttpClient {
    fn get(&mut self, url: impl Into<Url>) -> impl Future<Output = Response> + '_ {
        let url = url.into();
        async {
            // perform the get
        }
    }
}

fn foo(client: &mut HttpClient) -> impl Future<Output = Response> + '_ {
    let url = Url::parse("http://foo.example.com").unwrap();
    client.get(&url)
}

이렇게 하면 get 가 반환된 impl Future 임시 참조의 수명을 포함하기 때문에 error[E0597]: `url` does not live long enough 를 얻게 됩니다. 이 예제는 get 에 값으로 url을 전달할 수 있다는 점에서 약간 고안되었지만 실제 코드에서 유사한 경우가 거의 확실히 나타날 것입니다.

내가 말할 수 있는 한, 이에 대한 예상 수정은 추상 유형, 특히

impl HttpClient {
    abstract type Get<'a>: impl Future<Output = Response> + 'a;
    fn get(&mut self, url: impl Into<Url>) -> Self::Get<'_> {
        let url = url.into();
        async {
            // perform the get
        }
    }
}

간접 참조 계층을 추가하여 추상 유형에 필요한 제네릭 유형 및 수명 매개변수를 명시적으로 전달해야 합니다.

잠재적으로 이것을 작성하는 더 간결한 방법이 있는지 궁금합니다. 아니면 거의 모든 함수에 추상 유형이 사용되고 impl Trait 반환 유형이 절대 사용되지 않는 것으로 끝날까요?

내가 그 문제에의 코멘트를 @cramertj 이해한다면, 당신의 정의에 오류가 얻을 것이다 HttpClient::get 같은 `get` returns an `impl Future` type which is bounded to live for `'_`, but this type could potentially contain data with a shorter lifetime inside the type of `url` . (RFC는 impl Trait _all_ 일반 유형 매개변수를 캡처하도록 명시적으로 지정하고 명시적으로 선언된 수명보다 짧은 수명을 포함할 수 있는 유형을 캡처할 수 있는 버그입니다.)

여기에서 유일한 수정 사항은 캡처되는 형식 매개 변수를 명시적으로 선언할 수 있도록 명목 추상 형식을 선언하는 것입니다.

사실, 그것은 획기적인 변화가 될 것 같습니다. 따라서 이 경우에 오류가 추가될 예정이라면 빨리 하는 것이 좋습니다.

편집: 그리고 주석을 다시 읽으면 그것이 말하는 것이 아니라고 생각합니다. 그래서 추상 유형을 사용하지 않고 이 문제를 해결할 수 있는 잠재적인 방법이 있는지 여부에 대해 여전히 혼란스럽습니다.

@Nemo157 예, #42940 을 수정하면 Url 의 수명에 관계없이 반환 유형이 self의 차용만큼 오래 지속되도록 지정할 수 있으므로 평생 문제를 해결할 수 있습니다. 이것은 확실히 우리가 만들고자 하는 변경 사항이지만 그렇게 하는 것이 이전 버전과 호환된다고 생각합니다. 반환 형식의 수명이 더 짧아지는 것을 허용하지 않고 반환 형식을 사용할 수 있는 방법을 과도하게 제한합니다.

예를 들어, " Iter 매개변수가 충분히 오래 지속되지 않을 수 있습니다"라는 다음 오류:

fn foo<'a, Iter>(_: &'a mut u32, iter: Iter) -> impl Iterator<Item = u32> + 'a
    where Iter: Iterator<Item = u32>
{
    iter
}

함수에 대한 제네릭에 Iter 가 있는 것만으로는 반환 유형에 존재하도록 허용하기에 충분하지 않지만 현재 함수의 호출자는 그것이 있다고 잘못 가정합니다. 이것은 확실히 버그이며 수정되어야 하지만 이전 버전과 호환되도록 수정할 수 있으며 안정화를 차단해서는 안 된다고 생각합니다.

#46541 완료되었습니다. 누군가 OP를 업데이트할 수 있습니까?

type Foo = impl ...; 보다 abstract type Foo = ...; 구문을 선택한 이유가 있습니까? 나는 구문의 일관성을 위해 후자를 훨씬 선호했으며 이에 대해 얼마 전에 논의한 것을 기억하지만 찾을 수 없는 것 같습니다.

나는 type Foo = impl ...; 또는 type Foo: ...; , abstract 는 불필요한 괴짜처럼 보입니다.

제 기억이 맞다면 주요 관심사 중 하나는 사람들이 type X = Y 를 텍스트 대체(" X 되는 경우 Y 로 교체")처럼 해석하는 법을 배웠다는 것입니다. type X = impl Y 에서는 작동하지 않습니다.

내 직감이 typelet 처럼 작동하기 때문에 나는 type X = impl Y 나 자신을 선호하지만...

@alexreg RFC 2071 주제에 대해 많은 토론이 있습니다. TL;DR: type Foo = impl Trait;impl Trait 를 "좀 더 명시적인" 형식으로 제거하는 기능을 중단하고 약간 더 똑똑한 구문 대체로 작동하는 유형 별칭에 대한 사람들의 직관을 깨뜨립니다.

저는 Foo = impl ...; 또는 유형 Foo: ...;, 추상은 불필요한 괴짜처럼 보입니다.

exists type Foo: Trait; 캠프에 참여해야 합니다.wink:

@cramertj 흠. 나는 그 중 일부에 대해 스스로를 새로 고쳤고 솔직히 말해서 https://github.com/rust-lang/rfcs/pull/2071#issuecomment -319012123과 같은 작업을 수행하는 데 가장 적합하다고 생각합니다. 현재 구문에서도 이 작업을 수행할 수 있습니까?

exists type Foo: Trait; 는 약간 개선되었지만 여전히 exists 키워드를 삭제합니다. type Foo: Trait; 는 불평할 만큼 나를 괴롭히지 않을 것입니다. 😉 abstract 불필요 / 괴짜, @eddyb 말한다 단지되어있다.

@alexreg

이것은 현재 구문에서도 수행할 수 있습니까?

예, 하지만 훨씬 더 어색합니다. 이것이 = impl Trait 구문(모듈로 abstract 키워드)을 선호하는 주된 이유였습니다.

type Foo = (impl Bar, impl Baz);
type IterDisplay = impl Iterator<Item=impl Display>;

// can be written like this:

exists type Foo1: Bar;
exists type Foo2: Baz;
exists type Foo: (Foo1, Foo2);

exists type IterDisplayItem: Display;
exists type IterDisplay: Iterator<Item=IterDisplayItem>;

편집: 위의 exists type Foo: (Foo1, Foo2);type Foo = (Foo1, Foo2); 이어야 합니다. 혼란을 드려 죄송합니다.

@cramertj 구문이 좋아 보입니다. exists 가 적절한 키워드여야 합니까?

@cramertj 맞습니다. 그런 일을 해야 한다고 생각했습니다... = impl Trait 을 선호하는 좋은 이유가 있다고 생각합니다! 솔직히 말해서, 만약 사람들이 대체에 대한 직관이 여기에서 실존적 유형에 대해 충분히 무너졌다고 생각한다면(단순한 유형 별칭과 비교하여), 왜 다음 절충안이 안 될까요?

exists type Foo = (impl Bar, impl Baz);

(솔직히 저는 모든 것에 단일 type 키워드를 사용하는 일관성을 유지하고 싶습니다.)

나는 찾는다:

exists type Foo: (Foo1, Foo2);

깊이 이상하다. 사용 Foo: (Foo1, Foo2) 우변이 아닌 바운드 방법과 일치하지 않는 경우 Ty: Bound 다른 언어로 사용됩니다.

다음 형식은 나에게 괜찮아 보입니다.

exists type Foo: Bar + Baz;  // <=> "There exists a type Foo which satisfies Bar and Baz."
                             // Reads super well!

type Foo = impl Bar + Baz;

type Bar = (impl Foo, impl Bar);

나는 또한 여기서 abstract 를 단어로 사용하지 않는 것을 선호합니다.

exists type Foo: (Foo1, Foo2); 정말 이상합니다

그것은 확실히 나에게 실수처럼 보이며 type Foo = (Foo1, Foo2); 라고 말해야 한다고 생각합니다.

여기에서 abstract typeexists type 한다면 저는 전자를 확실히 지지할 것입니다. 대부분 "추상"이 형용사로 작동하기 때문입니다. 나는 대화에서 어떤 것을 "추상형"이라고 쉽게 부를 수 있지만, 우리가 "존재하는 유형"을 만든다고 말하는 것이 이상하게 느껴진다.

나는 또한 : (Foo, Bar) , = Foo + Bar , = impl Foo + Bar 또는 = (impl Foo, impl Bar : Foo + Bar 를 선호합니다. + 의 사용법은 경계가 될 수 있는 다른 모든 위치와 잘 작동하며 = 가 없다는 것은 전체 유형을 작성할 수 없다는 것을 의미합니다. 여기서 우리는 유형 별칭을 만드는 것이 아니라 특정 범위를 보장하지만 명시적으로 이름을 지정할 수는 없는 이름을 만들고 있습니다.


나는 또한 https://github.com/rust-lang/rfcs/pull/2071#issuecomment -318852774의 구문 제안을 여전히 좋아합니다.

type ExistentialFoo: Bar;
type Bar: Baz + Bax;

이것은 해당 스레드에서 언급했듯이 차이가 너무 적고 명확하지 않습니다.

나는 (impl Foo, impl Bar) 를 여러분 중 일부와 매우 다르게 해석해야 합니다... 나에게 이것은 유형이 일부 실존 유형의 2-튜플이고 impl Foo + Bar 와 완전히 다르다는 것을 의미합니다.

@alexreg 그것이 @cramertj 의 의도 : 구문에서 여전히 매우 이상하다는 것을 알게 될 것입니다.

exists type Foo: (Foo1, Foo2);

그것이 하는 일에 대해서는 여전히 매우 불분명해 보입니다. 경계는 일반적으로 어떤 경우에도 가능한 유형의 튜플을 지정하지 않으며 Foo: Foo1 + Foo2 구문의 의미와 쉽게 혼동될 수 있습니다.

= (impl Foo, impl Bar) 는 흥미로운 아이디어입니다. 자체적으로 알려지지 않은 유형으로 실존적 튜플을 생성하는 것은 흥미로울 것입니다. impl Fooimpl Bar 대한 두 개의 실존 유형을 도입한 다음 튜플에 대한 세 번째 유형 별칭을 도입할 수 있기 때문에 우리가 이들을 지원할 _필요_하다고 생각하지 않습니다.

@daboross 글쎄, 당신은 "exists type"이 아니라 "existential type"을 만들고 있습니다 . 이것이 유형 이론에서 호출되는 것입니다. 그러나 "...라는 유형의 Foo가 존재합니다." 라는 문구는 멘탈 모델과 유형 이론적인 관점 모두에서 잘 작동한다고 생각합니다.

하지만 impl Fooimpl Bar 대한 두 개의 실존 유형을 도입한 다음 튜플에 대한 세 번째 유형 별칭을 도입할 수 있으므로 이를 지원할 필요가 없다고 생각합니다.

그것은 인체 공학적이지 않은 것 같습니다 ... 임시는 그렇게 좋지 않습니다.

@alexreg 참고: impl Bar + Baz;(impl Foo, impl Bar) 와 같다고 말하려는 것은 아닙니다. 후자는 분명히 2-튜플입니다.

@daboross

그것이 @cramertj 의 의도

exists type Foo: (Foo1, Foo2);

그것이 하는 일에 대해서는 여전히 매우 불분명해 보입니다. 경계는 일반적으로 어떤 경우에도 가능한 유형의 튜플을 지정하지 않으며 Foo: Foo1 + Foo2 구문의 의미와 쉽게 혼동될 수 있습니다.

약간 불명확할 수도 있습니다( (impl Foo, impl Bar) 처럼 명시적이지 않습니다. 직관적으로 바로 이해할 수 있습니다). 하지만 개인적으로 Foo1 + Foo2 와 혼동하지 않을 것 같습니다.

= (impl Foo, impl Bar)는 흥미로운 아이디어입니다. 자체적으로 알려지지 않은 유형으로 실존적 튜플을 생성하는 것은 흥미로울 것입니다. impl Foo 및 impl Bar에 대해 두 가지 실존 유형을 도입한 다음 튜플에 대한 세 번째 유형 별칭을 도입할 수 있기 때문에 우리가 이를 지원할 필요가 없다고 생각합니다.

네, 그것은 초기 제안이었고, 저는 여전히 그것을 상당히 좋아합니다. 이것은 현재 구문을 사용하여 어쨌든 수행할 수 있지만 3줄의 코드가 필요하며 이는 인체공학적이지 않습니다. 나는 또한 ... = (impl Foo, impl Bar) 와 같은 구문이 사용자에게 가장 명확하다고 주장하지만 여기에 경합이 있다는 것을 알고 있습니다.

@Centril 처음에는 그렇게 생각하지 같더라고요 . 어쨌든, 우리가 그것을 해결해서 기쁩니다.

이런, https://github.com/rust-lang/rust/issues/34511#issuecomment -386763340에 대한 내 편집을 exists type Foo: (Foo1, Foo2);type Foo = (Foo1, Foo2); 이어야 합니다.

@cramertj 아, 이제 더 이해가 됩니다. 어쨌든 다음을 할 수 있는 것이 가장 인체공학적이라고 생각하지 않습니까? 다른 스레드를 검색하더라도 이에 대한 좋은 주장을 본 적이 없습니다.

type A = impl Foo;
type B = (impl Foo, impl Bar, String);

@alexreg 네, 그게 가장 인체 공학적인 구문입니다 생각하십니까.

RFC https://github.com/rust-lang/rfcs/pull/2289 를 사용하여 @cramertj 의 스니펫을 다시 작성하는 방법은 다음과 같습니다.

type Foo = (impl Bar, impl Baz);
type IterDisplay = impl Iterator<Item: Display>;

// alternatively:

exists type IterDisplay: Iterator<Item: Display>;

type IterDisplay: Iterator<Item: Display>;

그러나 유형 별칭의 경우 exists 도입하지 않으면 언어의 구문을 불필요하게 복잡하게 만들지 않으면서 표현력을 유지하는 데 도움이 될 것이라고 생각합니다. 따라서 복잡성 예산 POV에서 impl Iteratorexists 보다 나은 것 같습니다. 그러나 마지막 대안은 실제로 새로운 구문을 도입하지 않으며 명확하면서도 가장 짧습니다.

요약하면 다음 두 형식이 모두 허용되어야 한다고 생각합니다( impl Trait 및 이미 가지고 있는 관련 유형 구문의 범위 모두에서 작동하기 때문에).

type Foo = (impl Bar, impl Baz);
type IterDisplay: Iterator<Item: Display>;

편집: 어떤 구문을 사용해야 합니까? IMO, clippy는 가장 인체 공학적이고 직접적인 사용이 가능할 때 Type: Bound 구문을 명확하게 선호해야 합니다.

내가 많이 선호 type Foo: Trait 오버 변종 type Foo = impl Trait 변형. 연관된 유형 구문과 일치하는데, 이는 포함하는 모듈의 "출력 유형"이기도 하기 때문에 좋습니다.

impl Trait 구문은 이미 입력 및 출력 유형 모두에 사용되며, 이는 다형성 모듈의 인상을 줄 위험이 있음을 의미합니다. :(

impl Trait 가 출력 유형 에만 사용된 경우 연결된 유형 구문이 특성(ML 서명에 느슨하게 해당)에 더 가깝다는 이유로 type Foo = impl Trait 변형을 선호하는 반면 type Foo = .. 구문은 구체적인 모듈에 더 적합합니다.

@rpjohnst

내가 많이 선호 type Foo: Trait 오버 변종 type Foo = impl Trait 변형.

동의합니다. 가능할 때마다 사용해야 합니다. 그러나 바인딩된 구문을 직접 사용할 수 없는 (impl T, impl U) 경우는 어떻습니까? 임시 유형 별칭을 도입하면 가독성이 떨어지는 것 같습니다.

type Name: Bound 사용하면 impl 블록 내에서 사용될 때 혼란스러울 것 같습니다.

impl Iterator for Foo {
    type Item: Display;

    fn next(&mut self) -> Option<Self::Item> { Some(5) }
}

해당 구문과 키워드 접두사의 현재(?) 계획 모두 impl 블록에서 사용할 임시 유형 별칭을 도입하는 비용도 훨씬 더 큽니다. 이제 이러한 유형 별칭을 모듈 수준에서 내보내야 합니다( 그리고 의미적으로 의미 있는 이름이 주어지면...), 이는 개인 모듈 내에서 특성 구현을 정의하는 비교적 일반적인 패턴(적어도 저에게는)을 차단합니다.

pub abstract type First: Display;
pub abstract type Second: Debug;

impl Iterator for Foo {
    type Item = (First, Second);

    fn next(&mut self) -> Option<Self::Item> { Some((5, 6)) }
}

impl Iterator for Foo {
    type Item = (impl Display, impl Debug);

    fn next(&mut self) -> Option<Self::Item> { Some((5, 6)) }
}

@Nemo157 둘 다 허용하지 않는 이유:

pub type First: Display;
pub type Second: Debug;

impl Iterator for Foo {
    type Item = (First, Second);
    fn next(&mut self) -> Option<Self::Item> { Some((5, 6)) }
}

그리고:

impl Iterator for Foo {
    type Item = (impl Display, impl Debug);
    fn next(&mut self) -> Option<Self::Item> { Some((5, 6)) }
}

?

동일한 기능에 대해 두 개의 구문이 필요한 이유를 모르겠습니다. type Name = impl Bound; 구문을 사용하여 두 부분에 대한 이름을 명시적으로 제공하는 것은 여전히 ​​가능합니다.

pub type First = impl Display;
pub type Second = impl Debug;

impl Iterator for Foo {
    type Item = (First, Second);
    fn next(&mut self) -> Option<Self::Item> { Some((5, 6)) }
}

@ Nemo157 두 가지 다른 구문이 필요하지 않다는 데 동의합니다. 하지만 type (접두어 키워드 없음)이 전혀 혼동되지 않는다고 말해야 합니다.

@rpjohnst 다형성 모듈은 도대체 무엇입니까? :-) 어쨌든, 유형에 특성 경계 를 배치하는 연관된 유형 정의 이후에 구문을 모델링해야 하는 이유를 모르겠습니다. 이것은 bounds 와 관련이 없습니다.

@alexreg 다형성 모듈은 fn foo(x: impl Trait) 와 같은 방식으로 유형 매개변수가 있는 모듈입니다. 그것은 존재하는 것이 아니므로 사람들이 그렇게 생각하기를 원하지 않습니다.

abstract type ( 편집 : 키워드 사용을 제안하지 않고 기능의 이름을 지정하기 위해 ) 모든 것이 범위와 관련이 있습니다! 경계는 유형에 대해 아는 유일한 것입니다. 그것들과 연관된 유형 간의 유일한 차이점은 일반적으로 이름을 지정할 수 없기 때문에 유추된다는 것입니다.

@Nemo157 Foo: Bar 구문은 이미 다른 컨텍스트(연관된 유형 및 유형 매개변수에 대한 경계)에서 더 친숙하고 임시 구문을 도입하지 않고 사용할 수 있을 때 더 인체 공학적이고 (IMO) 명확합니다.

글쓰기:

type IterDisplay: Iterator<Item: Display>;

훨씬 더 직접적인 wrt로 보입니다. 내가 말하고 싶은 것, 그에 비해

type IterDisplay = impl Iterator<Item = impl Display>;

나는 이것이 우리가 이미 가지고 있는 구문을 일관되게 적용하고 있다고 생각합니다. 그래서 그것은 정말로 새로운 것이 아닙니다.

EDIT2: 첫 번째 구문은 또한 내가 rustdoc에서 렌더링되기를 원하는 방식입니다.

연관된 유형에서 무언가를 필요로 하는 특성에서 impl로 이동하는 것도 정말 쉽습니다.

trait Foo {
    type Bar: Baz;
    // stuff...
}

struct Quux;

impl Foo for Quux {
    type Bar: Baz; // Oh look! Same as in the trait; I had to do nothing!
    // stuff...
}

impl Bar 구문은 임시를 도입해야 할 때 더 나은 것처럼 보이지만 전체적으로 일관되게 구문을 적용합니다.

두 구문을 모두 사용할 수 있다는 것은 인수 위치에 impl Trait 를 사용할 수 있을 뿐만 아니라 인수에 의해 사용되는 명시적 유형 매개변수 T: Trait 를 갖는 것과 크게 다르지 않습니다.

EDIT1: 사실, 하나의 구문만 사용하는 것은 특별한 경우가 아니라 그 반대의 경우입니다.

@rpjohnst 나는 그것이 경계와 명시 적으로 관련 이 없다고 말했어야했지만 다르게 간청합니다.

어쨌든, 나는 type Foo: Bar; 구문에 반대하지 않지만, 선의를 위해 abstract 키워드를 제거합시다. type 자체는 어떤 상황에서도 매우 명확합니다.

개인적으로 =impl 를 사용하는 것은 추론이 일어나고 있음을 시각적으로 보여주는 좋은 힌트라고 생각합니다. 또한 더 큰 파일을 훑어볼 때 해당 위치를 쉽게 찾을 수 있습니다.

또한 type Iter: Iterator<Item = Foo> 있다고 가정하고 Foo 를 찾아보고 무슨 일이 일어나는지 알기 전에 그것이 유형인지 특성인지 파악해야 합니다.

그리고 마지막으로 추론 포인트의 시각적 단서는 추론 오류를 디버깅하고 추론 오류 메시지를 해석하는 데 도움이 될 것이라고 생각합니다.

그래서 = / impl 변형이 더 많은 페이퍼컷을 해결한다고 생각합니다.

@phaylon

또한, Iter: Iterator<Item = Foo> 유형이 있다고 가정하고 Foo 를 찾고 무슨 일이 일어나고 있는지 알기 전에 그것이 유형인지 또는 특성인지 파악해야 합니다.

이것은 내가 이해하지 못한다. Item = Foo dyn Foo 가 안정적이고 베어 트레잇이 단계적으로 사라지고 있다는 점을 감안할 때 Item = Foo 는 오늘날 항상 유형이어야 합니까?

@센트릴

이것은 내가 이해하지 못한다. Item = Foo는 dyn Foo가 안정적이라는 점을 감안할 때 항상 유형이어야 합니다(그리고 베어 트레잇이 단계적으로 사라지고 있습니다...)?

예, 하지만 제안된 impl 덜 변형에서는 경계가 있는 유추 유형 또는 구체적인 유형이 될 수 있습니다. 예: Iterator<Item = String>Iterator<Item = Display> . 추론이 일어나고 있는지 알기 위해서는 특성을 알아야 합니다.

편집: 아, : 사용된 것을 눈치채지 못했습니다. 놓치기 쉽다는 말이 무슨 뜻인가요 :) 하지만 그것들이 다르다는 것은 맞습니다.

편집 2: 이 문제는 연결된 유형 외부에서 유지될 것이라고 생각합니다. type Foo: (Bar, Baz) 주어지면 추론이 일어나는 위치를 알기 위해서는 Bar와 Baz를 알아야 합니다.

@센트릴

EDIT1: 사실, 하나의 구문만 사용하는 것은 특별한 경우가 아니라 그 반대의 경우입니다.

현재 -> impl Trait _existential_ 유형을 선언하는 한 가지 방법이 있습니다. _universal_ 유형을 선언하는 두 가지 방법이 있습니다(인수 목록에서 T: Trait: impl Trait ).

범용 유형을 취하는 다형성 모듈이 있는 경우 이에 대한 몇 가지 인수를 볼 수 있지만 모듈과 특성 정의 모두에서 현재 type Name = Type; 은 출력 유형 매개변수로, 존재해야 하는 출력 유형 매개변수라고 생각합니다. 유형.


@phaylon

예, 하지만 제안된 impl 덜 변형에서는 경계가 있는 유추 유형 또는 구체적인 유형이 될 수 있습니다. 예: Iterator<Item = String>Iterator<Item = Display> . 추론이 일어나고 있는지 알기 위해서는 특성을 알아야 합니다.

impl 덜 변종은 실존적 유형에 대해 모든 경우에 : Bound 를 사용하므로 Iterator<Item = String> 또는 Iterator<Item: Display> 를 특성 경계로 가질 수 있지만 Iterator<Item = Display> 는 잘못된 선언입니다.

@Nemo157
관련 유형 사례에 대해 당신이 옳습니다. 그러나 (내 편집에서 언급했듯이) type Foo: (A, B) 여전히 문제가 있다고 생각합니다. A 또는 B 는 유형 또는 특성이 될 수 있습니다.

이것이 = 로 가야 하는 좋은 이유이기도 합니다. : 는 일부 항목이 유추되었다는 것만 알려주지만 어느 항목인지는 알려주지 않습니다. type Foo = (A, impl B) 더 명확해 보입니다.

또한 impl 하여 코드 스니펫을 읽고 제공하는 것이 더 쉽다고 가정합니다. 특성이 무엇이고 제공할 필요가 없는 것이 무엇인지에 대한 추가 컨텍스트입니다.

편집: 일부 크레딧: 내 주장은 기본적으로 @alexreghere 와 동일합니다. impl 가 선호되는 이유를 확장하고 싶었습니다.

현재 존재 유형을 선언하는 방법은 -> impl Trait 뿐입니다. 범용 유형을 선언하는 두 가지 방법이 있습니다(인수 목록에서 T: Trait: impl Trait ).

그것이 내가 말하는 것입니다 : PI 왜 보편적 정량화에는 두 가지 방법이 있어야하지만 다른 곳 에서는 실존적으로 하나만 있어야합니까 ( dyn Trait 무시 )?

사용자가 언어의 다른 부분을 배운 후에 type Foo: Bound;type Foo = impl Bound; 를 작성할 가능성이 똑같이 보이며 모든 경우에 하나의 구문이 확실히 더 낫다고 말할 수는 없습니다. 한 구문이 어떤 경우에는 더 좋고 다른 구문은 다른 경우에 더 좋습니다.

@phaylon

나는 이것이 또한 =와 함께 가야 하는 좋은 이유라고 생각합니다. : 일부가 유추되었다는 것만 알려줄 뿐 어느 것이인지는 알려주지 않습니다. type Foo = (A, impl B)가 더 명확해 보입니다.

예, 이것은 아마도 또 다른 좋은 이유일 것입니다. 정의에서 정의로 점프하는 등 실존적으로 정량화되고 있는 것이 무엇인지 파악하려면 실제로 약간의 압축을 풀어야 합니다.

또 다른 점은 해당 구문에서 연결된 유형 바인딩 내에서 : 허용합니까? 이 제안된 구문에서 실존 유형이 다른 방식으로 합성/결합될 수 없다는 점을 감안할 때 이것은 나에게 약간 이상한 특별한 경우처럼 보일 것입니다. 다음이 해당 구문을 사용하는 가장 일관된 접근 방식이 될 것이라고 상상합니다.

type A: Foo;
type B: Bar;
type C: Baz;
type D: Iterator<Item = C>; 
type E = (A, Vec<B>, D);

내가 선호하는 구문을 사용하면 이 모든 것을 한 줄에 작성할 수 있으며 더 나아가 정량화가 일어나는 위치를 즉시 알 수 있습니다!

type E = (impl Foo, Vec<impl Bar>, impl Iterator<Item = impl Baz>);

위와 관련 없음: 야간에 let x: impl Trait 를 구현하기 위해 언제 플레이합니까? 나는 잠시 동안 이 기능을 놓치고 있었다.

@alexreg

또 다른 점은 해당 구문에서 연결된 유형 바인딩 내에서 : 허용합니까?

네, 왜 안 돼요? 이것은 rust-lang/rfcs#2289 + type Foo: Bound 의 자연스러운 효과입니다.

다음을 수행할 수도 있습니다.

type E = (impl Foo, Vec<impl Bar>, impl Iterator<Item: Baz>);

@Centril 두 가지 대체 구문을 허용하는 것은 나쁜 생각이라고 생각합니다. "우리는 결정할 수 없으므로 둘 다 지지할 것" 증후군과 같은 냄새가 납니다. 그것들을 믹스 앤 매치하는 코드를 보면 정말 눈에 거슬릴 것입니다!

@Centril 나는 당신의 BTW의 RFC에서 @nikomatsakisimpl Iterator<Item = impl Baz> 쓰십시오. 멋지고 명시적입니다.

@alexreg 이것은 공정합니다.

그러나 (불)다행히도 (귀하의 관점에 따라) 우리는 이미 impl Trait 인수 위치에 "두 가지 대체 구문 허용"을 시작하여 Foo: Barimpl Bar 같은 의미로 작동합니다.

그것은 보편적인 정량화를 위한 것이지만 impl Trait 표기법은 그것이 이중성의 어느 쪽에 있는지에 대해 별로 신경 쓰지 않습니다. 결국, 우리는 any Traitsome Trait 하지 않았습니다.

우리가 이미 "우리는 결정할 수 없다"와 "이원성의 측면은 구문적으로 중요하지 않다 "라는 선택을 했다는 점을 감안할 때, 사용자가 이해하지 못하는 모든 곳에 "우리는 결정할 수 없다"를 적용하는 것이 일관성 있는 것 같습니다. "그런데 저기에 이렇게 쓸 수 있는데 왜 여기에서는 안 될까요?" ;)


추신:

답장. impl Iterator<Item = impl Baz> where 절에서 경계로 작동하지 않습니다. 따라서 Iter: Iterator<Item = impl Baz> 와 같이 혼합해야 합니다. 균일하게 작동하려면 Iter = impl Iterator<Item = impl Baz> 를 허용해야 합니다.

: Bound 를 사용하는 것도 = impl Bound 대신에 명시적이지만 더 짧습니다 ^,-
X = TyX: Ty 사이의 간격 차이가 구문을 읽기 쉽게 만든다고 생각합니다.

내 자신의 조언을 무시하고 RFC에서 이 대화를 계속합시다 ;)

그러나 (불)다행히도 (귀하의 관점에 따라) 우리는 이미 인수 위치에 impl Trait을 사용하여 "두 가지 대체 구문 허용"을 시작하여 Foo: Bar와 impl Bar가 동일한 의미로 작동하도록 했습니다.

우리는 그렇게 했지만 대칭/일관성의 관점에서 더 많이 선택했다고 생각합니다. 일반 형식의 인수는 범용 형식( impl Trait ) 인수보다 엄격하게 더 강력합니다. 그러나 우리는 반환 위치에 impl Trait 를 도입하고 있었습니다. 이를 인수 위치에 도입하는 것이 합리적이었습니다.

우리가 이미 "우리는 결정할 수 없다"와 "이원성의 측면은 구문적으로 중요하지 않다"라는 선택을 했다는 점을 감안할 때, 사용자가 "그런데 저기에 이렇게 쓸 수 있는데 왜 여기에서는 안 될까요?" ;)

나는 우리가 팔을 들고 "모든 것을 구현하자"라고 말해야 할 시점인지 확신하지 못합니다. 이득에 대한 명확한 논거는 없습니다.

추신:

답장. impl Iterator<Item = impl Baz> where 절에서 경계로 작동하지 않습니다. 따라서 Iter: Iterator<Item = impl Baz> 와 같이 혼합해야 합니다. 균일하게 작동하려면 Iter = impl Iterator<Item = impl Baz> 를 허용해야 합니다.

나는 우리가 where Iter: Iterator<Item = T>, T: Baz 지원하거나(지금처럼) Iter = impl Iterator<Item = impl Baz> (당신이 제안한 대로)를 계속 진행한다고 말하고 싶습니다. 중간 집만 허용하는 것은 약간의 경찰 아웃처럼 보입니다.

: Bound 를 사용하는 것도 = impl 대신 명시적입니다. 다만 더 짧습니다 ^,-
X = TyX: Ty 사이의 간격 차이가 구문을 읽기 쉽게 만든다고 생각합니다.

읽을 수 있지만 존재 유형이 사용되고 있다는 것이 거의 명확하거나 명시적이라고 생각하지 않습니다. 이 구문의 제한으로 인해 정의가 여러 줄에 걸쳐 분할되어야 하는 경우 이는 악화됩니다.

내 자신의 조언을 무시하고 RFC에서 이 대화를 계속합시다 ;)

잠깐, 당신의 RFC를 의미합니까? 내가 말할 수 있는 바에 따르면 그것은 그것과 이것 모두에 관련이 있다고 생각합니다. :-)

잠깐, 당신의 RFC를 의미합니까? 내가 말할 수 있는 바에 따르면 그것은 그것과 이것 모두에 관련이 있다고 생각합니다. :-)

확인; 여기에서 계속합시다.

우리는 그렇게 했지만 대칭/일관성의 관점에서 더 많이 선택했다고 생각합니다. 일반 형식의 인수는 범용 형식( impl Trait ) 인수보다 엄격하게 더 강력합니다. 그러나 우리는 반환 위치에 impl Trait 를 도입하고 있었습니다. 이를 인수 위치에 도입하는 것이 합리적이었습니다.

내 요점은 일관성과 대칭에 관한 것입니다. =피
실존적 수량화와 보편적 수량화 모두에 대해 impl Trait 를 쓸 수 있다면 보편적 수량화와 실존적 수량화 모두에 Type: Trait 를 사용할 수 있어야 한다는 것이 이해가 됩니다.

표현력에 관해서는 당신이 말했듯이 전자가 후자보다 더 강력하지만 반드시 그래야 하는 것은 아닙니다. AFAIK를 원하면 똑같이 강력할 수 있습니다.

fn foo(bar: impl Trait, baz: typeof bar) { // eww... but possible!
    ...
}

나는 우리가 팔을 들고 "모든 것을 구현하자"라고 말해야 할 시점인지 확신하지 못합니다. 이득에 대한 명확한 논거는 없습니다.

내 주장은 "이 구문은 다른 곳에서 사용할 수 있고 의미는 명확하지만 여기서는 쓸 수 없습니다"라는 사용자를 놀라게 하는 것은 두 가지 방법을 사용하는 것보다 비용이 더 많이 든다는 것입니다(어쨌든 둘 다 익숙해져야 합니다) ). 우리와 비슷한 일을했습니다 https://github.com/rust-lang/rfcs/pull/2300 (합병), https://github.com/rust-lang/rfcs/pull/2302 (PFCP), HTTPS //github.com/rust-lang/rfcs/pull/2175 (병합) 여기서 우리는 이전에 다른 방식으로 작성할 수

읽을 수 있지만 존재 유형이 사용되고 있다는 것이 거의 명확하거나 명시적이라고 생각하지 않습니다.

내가 보기에는 가독성이면 충분합니다. 나는 Rust가 "무엇보다 명시적" 이라고 생각하지 않으며 지나치게 장황한 것(너무 많이 사용될 때 구문이 발견됨)도 사용을 방해하는 비용이 든다고 생각하지 않습니다.
(자주 사용하기를 원하면 더 간결한 구문을 제공하십시오... cf ? 에 대한 뇌물로 .unwrap() ).

이 구문의 제한으로 인해 정의가 여러 줄에 걸쳐 분할되어야 하는 경우 이는 악화됩니다.

이것은 내가 이해하지 못한다. Assoc = impl Trait 는 단순히 전자가 더 길기 때문에 Assoc: Trait 보다 줄 분할이 훨씬 더 많이 발생해야 한다고 생각합니다.

나는 우리가 where Iter: Iterator<Item = T>, T: Baz 지원하거나(지금처럼) Iter = impl Iterator<Item = impl Baz> (당신이 제안한 대로)를 계속 진행한다고 말하고 싶습니다.
중간 집만 허용하는 것은 약간의 경찰 아웃처럼 보입니다.

바로!, 중간집/캅아웃 하지말고 구현하자 where Iter: Iterator<Item: Baz> ;)

@Centril 좋아, 당신은 주로 대칭/일관성 논쟁에서 나를 이겼습니다. 😉 두 가지 형태의 인체 공학도 도움이 됩니다. 하지만 머지 않아 두꺼운 보풀이 이 기능에 필수입니다.

내일 전체 답장으로 이것을 편집할 것입니다.

편집하다

@Centril이 지적했듯이 우리는 이미 : Trait (바운드) 구문을 사용하여 범용 유형을 지원합니다. 예

fn foo<T: Trait>(x: T) { ... }

"적절한" 또는 "물리화된" 보편적 유형과 함께, 예를 들어

fn foo(x: impl Trait) { ... }

물론 전자가 후자보다 더 강력하지만 후자가 필요한 전부일 때 후자가 더 명확하고 틀림없이 더 읽기 쉽습니다. 사실, 가능한 경우 후자의 형식을 선호하는 컴파일러 린트가 있어야 한다고 강력히 믿습니다.

이제 함수 반환 위치에도 이미 impl Trait 가 있습니다. 이는 존재 유형을 나타냅니다. 연관된 유형의 특성은 형식이 존재하며 이미 : Trait 구문을 사용합니다.

이, 내가 모두 현재 녹 보편적 인 유형의 적절하고 결합 된 형태, 그리고 실존 유형에 대한 적절하고 결합 된 형태의 마찬가지로 존재 (단지 현재 특성 내에서 후자가), 난 강력하게 우리를 생각해야 것의 존재를 주어이해야 실존적 유형의 적절한 형식과 바인딩된 형식 모두에 대한 지원을 특성 외부로 확장합니다. 즉, 일반적으로 그리고 관련 유형에 대해 다음을 지원해야

type A: Iterator<Item: Foo + Bar>;
type B = (impl Baz, impl Debug, String);

이 주석 에서 제안한 컴파일러 린트 동작도 두 번째로, 이는 야생에서 일반적인 존재 유형의 표현 변형을 크게 줄여야 합니다.

나는 여전히 하나의 키워드로 보편적이고 실존적인 수량화를 혼동하는 것이 실수라고 생각하고 일관성 주장이 나에게 효과가 없다고 생각합니다. 함수 시그니처에서 단일 키워드가 작동하는 유일한 이유는 컨텍스트가 필연적으로 각 위치에서 한 가지 형태의 수량화를 사용하도록 제한하기 때문입니다. 동일한 제약 조건이 없는 것으로 볼 수 있는 잠재적인 설탕이 있습니다.

struct Foo {
    pub foo: impl Display,
}

이것은 실존적 또는 보편적 정량화의 약칭입니까? 함수 서명에서 impl Trait 를 사용하여 파생된 직관으로는 어떻게 결정할 수 있는지 모르겠습니다. 실제로 두 가지 모두로 사용하려고 하면 이 위치에서 익명의 보편적 정량화가 쓸모가 없다는 것을 빨리 깨닫게 될 것이므로 실존적 정량이어야 하지만 함수 인수의 impl Trait 와 일치하지 않는 것처럼 보입니다.

이것들은 근본적으로 다른 두 가지 작업입니다. 예, 둘 다 특성 경계를 사용하지만 실존 유형을 선언하는 두 가지 방법을 사용하는 것이 신규 사용자의 혼란을 줄이는 데 도움이 될 이유는 없습니다. type Name: Trait 를 사용하려고 시도하는 것이 실제로 신규 이민자가 할 수 있는 일이라면 린트를 통해 해결할 수 있습니다.

    type Foo: Display;
    ^^^^^^^^^^^^^^^^^^
note: were you attempting to create an existential type?
note: suggested replacement `type Foo = impl Display`

그리고 방금 내가 훨씬 더 순응할 수 있는 귀하의 주장에 대한 대안적인 공식을 생각해 냈습니다. RFC를 다시 읽고 이에 대해 게시하려면 실제 컴퓨터에 있을 때까지 기다려야 합니다.

아직 RFC에 대해 논평할 만큼 Rust에 대한 경험이 충분하지 않은 것 같습니다. 그러나 Drops of Diamond 샤딩 구현의 일부로 이더리움용 샤딩 프로토콜을 구축하기 위해 Rust libp2p 와 함께 이 기능을 사용하기 위해 야간에 안정적인 Rust에 이 기능을 병합하는 데 관심이 있습니다. 나는 이 문제를 구독했지만 모든 댓글을 따라갈 시간이 없습니다! 댓글을 훑어보지 않고 이 문제에 대한 최신 정보를 얻으려면 어떻게 해야 합니까?

아직 RFC에 대해 논평할 만큼 Rust에 대한 경험이 충분하지 않은 것 같습니다. 그러나 Drops of Diamond 샤딩 구현의 일부로 이더리움용 샤딩 프로토콜을 구축하기 위해 Rust libp2p 와 함께 이 기능을 사용하기 위해 야간에 안정적인 Rust에 이 기능을 병합하는 데 관심이 있습니다. 나는 이 문제를 구독했지만 모든 댓글을 따라갈 시간이 없습니다! 댓글을 훑어보지 않고 이 문제에 대한 최신 정보를 얻으려면 어떻게 해야 합니까? 현재로서는 문제를 구독하지 않고 때때로 체크인하여 그렇게 해야 할 것 같습니다. 이에 대한 높은 수준의 뉴스를 받기 위해 이메일로 구독할 수 있다면 좋을 것입니다.

나는 여전히 하나의 키워드로 보편적이고 실존적인 수량화를 혼동하는 것이 실수라고 생각하고 일관성 주장이 나에게 맞지 않습니다.

일반적인 원칙으로서 이 기능과는 별개로 저는 이 추론 방식이 문제가 있음을 발견했습니다.

나는 우리가 역사의 어떤 대안적 전개 아래에서 우리가 원했던 방식 보다는 언어가 어떻게 존재하는지 에서 언어 디자인에 접근해야 한다고 믿습니다. 인수 위치에서 보편적인 수량화로서의 impl Trait 구문은 안정화되어 있으므로 이를 원하지 않습니다. X, Y, Z가 실수라고 생각하더라도(그리고 개인적으로 Rust의 설계에서 실수라고 생각하는 많은 부분을 찾을 수 있지만, 나는 그것을 받아들이고 가정합니다...), 우리는 지금 그것들과 함께 살아야 하고, 제 생각에는 새로운 기능이 주어지면 모든 것을 함께 맞추는 방법(일관성 있게 만들기).

토론에서 나는 RFC의 전체 코퍼스와 언어를 있는 그대로의 공리가 아니라 강력한 논거로 받아들여야 한다고 생각합니다.


당신은 다음과 같은 경우를 만들 수 있습니다 (그러나 나는 그렇게하지 않을 것입니다).

struct Foo {
    pub foo: impl Display,
}

다음과 의미상 동일합니다.

struct Foo<T: Display> {
    pub foo: T,
}

함수 인수 추론에서.

기본적으로 impl Trait 주어졌을 때 "이것이 반환 유형과 같습니까, 아니면 인수와 같습니까?"라고 생각해야 합니다. , 어려울 수 있습니다.


type Name: Trait 를 사용하려고 시도하는 것이 실제로 신규 이민자가 할 가능성이 있는 일이라면 린트를 통해 해결할 수 있습니다.

나는 또한 보푸라기지만 다른 방향으로; 다음 방법이 관용적이어야 한다고 생각합니다.

// GOOD:
type Foo: Iterator<Item: Display>;

type Bar = (impl Display, impl Debug);

// BAD
type Foo = impl Iterator<Item = impl Display>;

type Bar0: Display;
type Bar1: Debug;
type Bar = (Bar0, Bar1);

알겠습니다. RFC 2071 이 암시하고 있으며 이 문제에서 논의되었을 수 있지만 명시적으로 언급되지 않은 대체 공식입니다.

존재적으로 수량화된 유형을 선언하는 _한 가지 방법_이 있습니다. existential type Name: Bound; ( existential 를 사용하는 것은 RFC에 지정되어 있기 때문에 이 공식에서 키워드를 삭제하는 것에 전적으로 반대하는 것은 아닙니다).

현재 범위에서 이름 없는 실존적으로 수량화된 유형을 암시적으로 선언하기 위한 추가 설탕이 있습니다. impl Bound (당분간 함수 인수의 보편적인 수량화 설탕 무시).

따라서 현재 반환 유형 사용법은 간단한 디슈가링입니다.

fn foo() -> impl Iterator<Item = impl Display> { ... }
existential type _0: Display;
existential type _1: Iterator<Item = _0>;
fn foo() -> _1 { ... }

const , staticlet 것도 마찬가지로 간단합니다.

RFC에 언급되지 않은 한 가지 확장은 type Alias = Concrete; 구문에서 이 설탕을 지원하므로 다음을 작성할 때

type Foo = impl Iterator<Item = impl Display>;

이것은 실제로 설탕입니다

existential type _0: Display;
existential type _1: Iterator<Item = _0>;
type Foo = _1;

그런 다음 현재 모듈이 Foo 를 살펴보고 존재 유형을 참조하는지 확인하기 위해 유형 별칭의 투명한 특성에 의존합니다.

사실, 가능한 경우 후자의 형식을 선호하는 컴파일러 린트가 있어야 한다고 강력히 믿습니다.

나는 대부분 @alexreg 의 의견에 동의하지만 impl Trait 가 작동하지 않기 때문에 라이브러리에서 semver 중단 변경을 조장할 위험 때문에 arg: impl Trait 에 대한 linting에 대해 몇 가지 우려가 있습니다. 터보피시와 함께(지금 당장, 그리고 제대로 작동하려면 부분적인 터보피쉬가 필요합니다). 따라서 clippy의 linting은 type alias의 경우보다 덜 간단하게 느껴집니다(어떤 문제를 일으키는 Turbofish가 없는 경우).

나는 대부분 @alexreg 의 의견에 동의하지만 arg: impl Trait에 대한 linting에 대해 몇 가지 우려가 있습니다. 주로 impl Trait이 Turbofish와 함께 작동하지 않기 때문에 라이브러리에서 semver 중단 변경을 조장할 위험이 있기 때문입니다(지금은, 잘 작동하려면 부분적인 터보피쉬가 필요합니다.) 따라서 clippy의 linting은 type alias의 경우보다 덜 간단하게 느껴집니다(어떤 문제를 일으키는 Turbofish가 없는 경우).

@Centril이 방금 IRC에서 이 했으며 , 이전 버전과의 호환성(너무 깨지기 쉬운)에 관한 공정한 지적에 동의합니다. 부분적인 터보피시가 착륙하면 컴파일러 린트를 추가해야 한다고 생각하지만 그때까지는 아닙니다.

그래서... 우리는 지금 명명된 ​​실존 유형의 구문에 대해 꽤 많은 토론을 했습니다. 누군가가 실제 구현 작업을 시작할 수 있도록 결론을 내리고 RFC/PR 게시물에 작성해 볼까요? :-)

우리가 existentials 이름 일단 개인적으로, 나는 거리의 사용에서 (있는 경우) 보풀이 선호 impl Trait 어디서나.

@rpjohnst 글쎄, 당신은 명명된 실존에 관해서 저와 @Centril에 확실히 동의합니다... 함수 인수에서 그것들로부터 linting을 제거하는 것에 관해서는, 그것은 가능성이 있지만 아마도 다른 곳에서 논의될 것입니다. 이 맥락에서 단순성 또는 일반성을 선호하는지 여부에 따라 다릅니다.

인수 위치의 impl Trait 에 대한 RFC가 최신 상태입니까? 그렇다면 의미 체계가 _보편적_이라고 말하는 것이 안전합니까? 그렇다면: 나는 울고 싶다. 깊이.

@phaazon : impl Trait 대한 Rust 1.26 릴리스 정보 :

타자 이론가들을 위한 참고 사항: 이것은 실존적이지 않고 여전히 보편적입니다. 즉, impl Trait는 입력 위치에서는 보편적이지만 출력 위치에서는 존재합니다.

그것에 대한 내 생각을 표현하자면 다음과 같습니다.

  • 우리는 이미 유형 변수에 대한 구문을 가지고 있으며 실제로 무명의 유형 변수에 대한 몇 가지 용도가 있습니다(즉, 유형 변수를 한 곳에 놓는 대신 여러 위치에서 사용하려는 경우가 매우 많습니다).
  • 공변적 실존은 우리에게 rank-n 함수의 문을 열어줄 것입니다. 이는 특성 없이는 지금 당장은 하기 힘든 일이며( this 참조) Rust에는 정말 없는 기능입니다.
  • impl Trait 는 "호출 수신자가 선택한 유형"이라고 부르기 쉽습니다. 왜냐하면... 현재로서는 그렇게 할 수 있는 유일한 언어 구조이기 때문입니다! 호출자가 유형을 선택하는 것은 이미 여러 구문을 통해 사용할 수 있습니다.

나는 impl Trait 주장 입장에서 현재 결정이 유감이라고 생각합니다. :울다:

현재의 결정이 유감스럽게 생각합니다. 😢

나는 이것에 대해 약간 고민하고 있지만 지금 당장 let x: impl Trait 을 구현하는 데 시간을 보내는 것이 더 낫다고 생각합니다!

공변적 실존은 우리에게 rank-n 함수의 문을 열어줄 것입니다.

우리는 이미 이에 대한 구문( fn foo(f: impl for<T: Trait> Fn(T)) )(일명 "type HRTB")을 가지고 있지만 아직 구현되지 않았습니다. fn foo(f: impl Fn(impl Trait)) 는 "중첩된 impl Trait 는 허용되지 않습니다"라는 오류를 생성하며 HRTB 유형을 얻을 때 상위 버전을 의미하기를 원할 것으로 예상합니다.

이것은 Fn(&'_ T) for<'a> Fn(&'a T) 의미하는 것과 유사하므로 논란의 여지가 없을 것으로 예상합니다.

현재 초안을 보면 인수 위치의 impl Trait 가 _universal_인데 impl for<_> Trait 가 _existential_로 바뀌었다는 말씀이신가요?! 얼마나 미친 짓이야?

_보편적인_ 구성을 위해 _또 다른 방법_을 도입할 필요가 있다고 생각한 이유는 무엇입니까? 내 말은:

fn foo(x: impl MyTrait)

익명 유형 변수 는 유형에서 한 번만 나타나기 때문에 흥미롭습니다. 동일한 유형을 반환해야 하는 경우:

fn foo(x: impl Trait) -> impl Trait

분명히 작동하지 않을 것입니다. 우리는 사람들에게 단지 하나를 배우고 모든 곳에서 사용하는 대신 더 일반적인 관용구 에서 더 제한적인 부가가치추가 - RFC에서 읽은 학습 가정은 우리가 신규 사용자 대신 생각하는 이상한 주장입니다. 그들은 여전히 ​​학습해야 할 것이므로 구문을 거의 여기 학습 곡선을 낮추기 위해 모두? 사람들이 보편적인 것과 실존적인 것에 익숙해지면(그리고 하나가 되면 원칙은 올바른 단어로 매우 간단합니다), 사람들은 왜 우리가 동일한 키워드/패턴을 사용하여 둘과 where 를 표현해야 하는지 생각하기 시작할 것입니다. 및 제자리 템플릿 매개변수.

아아아아아아아아아아아아아아ㅏㅏㅏㅏㅏㅏㅏㅏㅏㅏㅏㅏㅏㅏㅏㅏㅏㅏㅏㅏㅏㅏㅏㅏㅏㅏㅏㅏㅏㅏㅏㅏㅏㅏㅏㅏㅏㅏㅏㅏㅏㅏㅏㅏㅏㅏㅏㅏㅏㅏㅏㅏㅏㅏㅏㅏㅏㅏㅏㅏㅏㅏㅏㅏㅏㅏㅏㅏㅏㅏㅏㅏㅏㅏㅏㅏㅏㅏㅏㅏㅏㅏ" 정말 안타깝다는 생각뿐입니다. RFC 결정에 실망한 사람은 나뿐만이 아닐 것이라고 확신합니다.

(기능이 안정화된 후에 이에 대해 계속 토론하는 것은 별로 의미가 없을 것입니다. 하지만 여기서 impl Trait 가 의미 체계를 갖는 인수 위치에 있는 것이 합리적이고 타당한 이유에 대한 설득력 있는 인수(동의합니다)는 여기fn foo(arg: Box<Trait>) 로 거의 같은 방식으로 작동 fn foo<T: Trait>(arg: Box<T>) 에도 불구하고, dyn Trait 실존이며, 지금 대체 dyn impl .)

현재 초안을 보면 impl Trait 논증 위치가 보편인데 impl for<_> Trait 가 실존으로 바뀐다고?!

아니요, 둘 다 보편적입니다. 더 높은 순위의 용도는 다음과 같습니다.

fn foo<F: for<G: Fn(X) -> Y> Fn(G) -> Z>(f: F) {...}

추가됨과 동시에(즉, impl Trait 대한 변경 없이) 다음과 같이 작성할 수 있습니다.

fn foo(f: impl for<G: Fn(X) -> Y> Fn(G) -> Z) {...}

그것은 보편적인 impl Trait 입니다. Trait 가 HRTB ( impl for<'a> Fn(&'a T) 와 유사)입니다.
우리가 (내가 가능성이 기대되는) 것을 결정하는 경우 impl Trait 내부 Fn(...) 인수가 보편적이며, 동일한 효과를 얻을이를 작성할 수 :

fn foo(f: impl Fn(impl Fn(X) -> Y) -> Z) {...}

이것이 내가 "상위 등급"을 의미한다고 생각한 것입니다. 그렇지 않은 경우 알려주십시오.

더욱 흥미로운 결정은, 실존 적 위치에 같은 치료를 적용 할 수 있도록하여 예이 (의미 "다른 폐쇄를 취 일부 폐쇄를 돌려"것이다) :

fn foo() -> impl for<G: Fn(X) -> Y> Fn(G) -> Z {...}

다음과 같이 작성됩니다.

fn foo() -> impl Fn(impl Fn(X) -> Y) -> Z {...}

그것은 보편적인 impl Trait 포함하는 실존적 impl Trait 일 것입니다.

@eddyb 일관성과 초보자를 혼동하지 않기 위해 일반적으로 실존적 및 보편적 정량화에 대해 두 개의 개별 키워드를 갖는 것이 더 합리적이지 않습니까?
실존적 수량화의 키워드는 실존적 유형에도 재사용 가능하지 않을까요?
실존적( 보편적) 수량화에는 impl 를 사용하지만 실존 유형에는 existential 를 사용하는 이유는 무엇입니까?

저는 세 가지 점을 지적하고 싶습니다.

  • impl Trait 가 실존적이거나 보편적인지 논의할 가치는 별로 없습니다. 대부분의 프로그래머는 유형 이론 핸드북을 충분히 읽지 않았을 것입니다. 문제는 사람들이 그것을 좋아하는지 아니면 혼란스러워하는지 입니다. 그 질문에 답하기 위해 여기 이 스레드, reddit 또는 포럼 에서 몇 가지 형태의 피드백을 볼 수 있습니다. 추가 설명이 필요한 경우 직관적이거나 놀랍지 않은 기능에 대한 리트머스 테스트에 실패합니다. 그래서 우리는 얼마나 많은 사람들이, 얼마나 혼란스러워하는지, 그리고 다른 기능보다 질문이 더 많은지 살펴봐야 합니다. 안정화 이후에 이런 피드백이 온다는 것이 참으로 안타깝고, 이 현상에 대해 대책을 세워야 하는 부분이지만, 별도의 논의를 위한 것입니다.
  • 기술적으로 안정화 후에도 이 경우 기능을 제거 수 있는 방법이 있습니다(필요한 경우 결정을 보류). 그것을 사용하는 쓰기 기능에 대해 린트하고 다음 판에서 기능을 제거하는 것이 가능할 것입니다(다른 판 상자에서 온 경우 호출하는 기능은 유지하면서). 그것은 녹 안정성 보장을 만족시킬 것입니다.
  • 아니요, 실존 및 범용 유형을 지정하기 위해 두 개의 키워드를 추가하면 혼란이 개선되지 않고 상황이 더 악화될 뿐입니다.

이 피드백이 안정화 후에 도착하는 것은 참으로 슬프고 이 현상에 대해 조치를 취해야 합니다.

impl Trait 이 아이디어인 한 주장 위치에 반대가 있었습니다. 이와 같은 피드백은 _새로운 것이 아닙니다_. 관련 RFC 스레드에서도 많은 논란이 되었습니다. 유형 이론의 관점에서 보편적/실존적 유형에 대한 논의뿐만 아니라 이것이 새로운 사용자에게 어떻게 혼란을 줄 것인지에 대해서도 많은 논의가 있었습니다.

사실, 우리는 실제 새로운 사용자의 관점을 얻지 못했지만 이것은 아무데도 나온 것이 아닙니다.

@Boscop anysome 가 이 작업을 수행하기 위한 한 쌍의 키워드로 제안 되었지만 반대로 결정되었습니다(근거가 어디에도 기록되었는지 여부는 알 수 없지만).

사실, 우리는 유형 이론가가 아닌 사람과 녹을 처음 접하는 사람들로부터 피드백을 얻을 수 없었습니다.

그리고 포함에 대한 인수는 이민자를위한 더 쉽게 만들 것이라고 항상이었다. 이제 우리가 신규 이민자로부터 실제 피드백을 받았다면 신규 이민자가 이를 어떻게 이해해야 하는지 논쟁하는 대신 매우 적절한 종류의 피드백이 되어야 하지 않을까요?

시간이 있다면 포럼이나 다른 곳을 통해 사람들이 얼마나 혼란스러워했는지 조사할 수 있었을 거라고 생각합니다(저는 통계를 잘 못하지만, 맹목적인 예측보다 더 나은 것을 생각해내십시오).

그리고 포함에 대한 주장은 항상 새로 온 사람들을 더 쉽게 만들 수 있다는 것이었습니다. 이제 우리가 신규 이민자로부터 실제 피드백을 받았다면 신규 이민자가 이를 어떻게 이해해야 하는지 논쟁하는 대신 매우 적절한 종류의 피드백이 되어야 하지 않을까요?

네? 나는 일어난 일이 좋은 생각인지 나쁜 생각인지 논쟁하는 것이 아닙니다. RFC 스레드가 이에 대한 피드백을 받았고 어쨌든 결정되었음을 지적하려는 것입니다.

당신이 말했듯이 피드백에 대한 메타 토론을 다른 곳에서 하는 것이 더 나을 것입니다. 하지만 그것이 어디인지 잘 모르겠습니다.

아니요, 실존 및 범용 유형을 지정하기 위해 두 개의 키워드를 더 추가하면 혼란이 개선되지 않고 상황이 더 악화될 뿐입니다.

더 나쁜? 어떻게 그렇습니까? 나는 모호함/혼돈보다 기억할 것이 더 많은 것을 선호합니다.

네? 나는 일어난 일이 좋은 생각인지 나쁜 생각인지 논쟁하는 것이 아닙니다. RFC 스레드가 이에 대한 피드백을 받았고 어쨌든 결정되었음을 지적하려는 것입니다.

확신하는. 그러나 논쟁을 벌이는 양쪽 모두는 내부에서 일어나는 일에 대해 깊이 이해하고 자신이 속하지 않은 그룹(신입자)에 대해 추측하고 미래에 대해 추측하는 늙고 상처가 있고 경험 많은 프로그래머였습니다. 사실적인 관점에서 볼 때 현실에서 실제로 일어나는 일에 관해서는 주사위를 굴리는 것보다 훨씬 낫지 않습니다. 이것은 전문가의 부적절한 경험에 대한 것이 아니라 결정의 기반이 되는 적절한 데이터가 없다는 것입니다.

이제 그것이 도입되었고 우리는 실제 하드 데이터, 또는 얼마나 많은 사람들이 0에서 10까지의 척도에서 혼란스러워하는지에 대한 토지에서 얻을 수 있는 가장 하드 데이터를 얻을 수 있는 방법을 갖게 되었습니다.

당신이 말했듯이 아마도 다른 곳에서 피드백에 대한 메타 토론을 갖는 것이 더 나을 것입니다.

예를 들어 여기에서 이미 그러한 논의를 시작했으며 작은 단계라도 취할 수 있는 몇 가지 실제 단계가 있습니다. https://internals.rust-lang.org/t/idea-mandate-n-independent-uses -기능을 안정화하기 전/7522/14. 나는 RFC를 작성할 시간이 없었기 때문에 누군가가 나를 때리거나 도와주고 싶어도 상관하지 않을 것입니다.

더 나쁜? 어떻게 그렇습니까?

impl Trait 가 더 이상 사용되지 않는 경우가 아니면 3가지 모두가 있으므로 혼란 과 함께 기억해야 할 것이 더 많기 때문입니다. impl Trait 가 사라지면 상황이 달라지고 두 접근 방식의 장단점에 가중치가 부여됩니다.

호출 수신자 선택에서와 같이 impl Trait 이면 충분합니다. 논쟁의 위치에서 사용하려고 하면 혼란을 야기합니다. HRTB는 그 혼란을 제거할 것입니다.

@vorner 이전에 Rust에 능숙한 사람으로서 추측하기 어렵기 때문에 Rust 초보자가 실제로 배우기가 더 쉽고 더 어렵다는 것을 확인하기 위해 Rust 초보자와 실제 A/B 테스트를 수행해야 한다고 주장했습니다.
FWIW, 내가 Rust(C++, D, Java 등에서 유래)를 배울 때, 보편적으로 수량화하는 유형 제네릭(구문 포함)은 이해하기 쉬웠던 기억이 납니다(제네릭의 수명은 조금 더 어려웠습니다).
나는 impl Trait for arg types 가 초보자 이와 같은 많은 질문에서 많은 혼란을 초래할 것이라고 생각 합니다 .
변경 사항이 Rust를 더 쉽게 배울 수 있는 증거가 없는 경우, 그러한 변경을 삼가해야 하며, 일관성이 최소한 기억하기 쉽도록 만들기 때문에 Rust를 보다 일관성 있게 만들고 유지하도록 변경해야 합니다. Rust 초보자는 어쨌든 이 책을 몇 번 읽어야 할 것이므로 책에서 제네릭을 나중으로 연기할 수 있도록 args에 impl Trait 를 도입하는 것은 실제로 복잡성을 없애지 않습니다.

@eddyb Btw, impl 외에 유형에 대해 또 다른 existential 키워드가 필요한 이유는 무엇입니까? (두 가지 모두에 some 를 사용했으면 합니다.)

FWIW, 내가 Rust(C++, D, Java 등에서 유래)를 배울 때, 보편적으로 수량화하는 유형 제네릭(구문 포함)은 이해하기 쉬웠던 기억이 납니다(제네릭의 수명은 조금 더 어려웠습니다).

나 자신도 그것이 문제라고 생각하지 않는다. 현재 회사에서 저는 Rust 수업을 이끌고 있습니다. 지금은 일주일에 한 번 만나 실제 구현을 가르치려고 합니다. 사람들은 대부분 Java 및 Scala 배경에서 온 노련한 프로그래머입니다. 몇 가지 장애물이 있었지만 인수 위치에 있는 제네릭(적어도 읽기 - 실제로 작성하는 데 약간 주의함)은 문제가 되지 않았습니다. 반환 위치에 있는 제네릭에 대해 약간의 놀라움이 있었습니다(예: 호출자가 함수가 반환하는 것을 선택함). 특히 종종 생략될 수 있지만 설명이 클릭되기까지 2분 정도 걸렸습니다. 그러나 나는 논쟁 위치에 impl Trait의 존재를 언급하는 것조차 두렵습니다. 왜냐하면 이제 나는 그것이 존재하는 이유에 대한 질문에 답해야 하고 그것에 대한 진정한 답이 없기 때문입니다. 그것은 동기 부여에 좋지 않으며 학습 과정에서 동기 부여가 중요합니다.

따라서 문제는 커뮤니티가 주장을 뒷받침할 일부 데이터로 토론을 재개할 수 있는 충분한 목소리를 갖고 있느냐는 것입니다.

@eddyb Btw, impl 외에 유형에 대해 다른 존재 키워드가 필요한 이유는 무엇입니까? (둘 다 사용했으면 좋았을텐데..)

forall ... /나 천천히 몰래

@phaazon 우리는 forall (즉, "범용")을 가지고 있으며 for . 예를 들어 HRTB( for<'a> Trait<'a> )입니다.

@eddyb 예, 예를 들어 Haskell이 forall 에서 하는 것처럼 실존적 으로도 사용하십시오.

전체 토론은 매우 독단적입니다. 나는 주장 아이디어가 안정되어 보이는 것에 약간 놀랐습니다. 나중에 다른 RFC를 푸시하여 이를 취소할 수 있는 방법이 있기를 바랍니다(이로 인해 발생할 혼란이 정말 정말 마음에 들지 않기 때문에 기꺼이 작성하겠습니다).

정말 이해가 되지 않습니다. 그들이 논쟁의 위치에 있게 하는 요점이 무엇입니까? 저는 Rust를 그렇게 많이 쓰지는 않지만 -> impl Trait 을 할 수 있다는 것이 정말 좋았습니다. 언제 논쟁 위치에서 그것을 사용합니까?

내 이해는 그것이 대부분 일관성을 위한 것이었다는 것입니다. fn 서명의 인수 위치에 impl Trait 유형을 쓸 수 있다면 왜 다른 곳에는 쓸 수 없습니까?

즉, 나는 개인적으로 사람들에게 "그냥 유형 매개 변수를 사용하십시오"라고 말했습니다 ...

예, 일관성을 위한 것입니다. 그러나 유형 매개변수를 사용하기 쉬운 경우 충분한 인수인지 확신할 수 없습니다. 또한 문제가 발생하면 찬성/반대합니다!

또한 문제가 발생하면 찬성/반대합니다!

impl Trait 로 여러 가지를 표현할 수 없다는 점을 고려하면 인수 중 하나로 impl Trait 를 사용하여 함수를 사용할 수 없으므로 해당 주소를 가져올 수 없습니다. 다른 단점은?), 어쨌든 사용해야 하기 때문에 유형 매개변수에 대해 린트하는 것은 거의 의미가 없다고 생각합니다.

따라서 주소를 가져올 수 없습니다.

서명에서 유추하여 수행할 수 있습니다.

그들이 논쟁의 위치에 있게 하는 요점이 무엇입니까?

특성 경계를 사용하는 것과 정확히 동일하기 때문에 아무 것도 없습니다.

fn foo(x: impl Debug)

와 정확히 같은가요?

fn foo<A>(x: A) where A: Debug
fn foo<A: Debug>(x: A)

또한 다음을 고려하십시오.

fn foo<A>(x: A) -> A where A: Debug

인수 위치의 impl Trait익명 처리되어 있기 때문에 이 작업을 수행할 수 없습니다. 우리는 이미 그러한 상황을 처리하는 데 필요한 모든 것을 갖추고 있기 때문에 이것은 꽤 쓸모없는 기능입니다. 거의 모든 사람이 유형 변수/템플릿 매개변수를 알고 있고 Rust가 해당 impl Trait 구문을 사용하는 단일 언어이기 때문에 사람들은 이 새로운 기능을 쉽게 배우지 못할 것입니다. 그렇기 때문에 많은 사람들이 반환 값 / let 바인딩에 남아 있어야 한다고 주장하는 이유는 이것이 필요한 새로운 의미 체계(즉, 호출 수신자가 선택한 유형)를 도입했기 때문입니다.

간단히 말해서 @iopq : 이것은 필요하지 않을 것이며 "매우 구체적인 용도, 즉 익명화된 유형 변수에 대처하기 때문에 아무도 실제로 필요로 하지 않는 또 다른 구문 설탕 구조를 추가합시다"라는 말 외에는 아무 의미가 없습니다.

또한, 내가 말하는 것을 잊은 것: 그것은 당신의 함수가 어떻게 매개변수화/단형화되는지 보는 것을 훨씬 더 어렵게 만듭니다.

@Verner 부분적인 터보

-> impl Trait 호출자가 유형을 선택하는 동안 x: impl Trait 호출자가 유형을 선택하면 일관성이 어떻게 됩니까?

나는 그것이 작동하는 다른 방법이 없다는 것을 이해하지만 그것은 "일관된"것 같지 않습니다. 일관성의 반대처럼 보입니다.

나는 그것이 일관성을 제외하고는 모든 것이며 사람들이 혼란스러워 할 것이라는 데 진심으로 동의합니다. 초보자는 물론 고급 녹슨 사람입니다.

우리는 이 스레드에서 다시 제기되는 질문을 해결하기 위해 2년 전에 시작하여 약 600개의 의견을 받은 두 개의 RFC를 받았습니다.

  • Rust-lang/rfcs#1522("최소 impl Trait ")
  • Rust-lang/rfcs#1951 (" impl Trait 구문과 매개변수 범위를 인수로 확장하면서 마무리")

(이 토론을 읽으면 내가 처음에 두 개의 키워드 접근 방식을 강력하게 지지했음을 알 수 있습니다. 이제는 단일 키워드를 사용하는 것이 올바른 접근 방식이라고 생각합니다.)

2년 동안 수백 개의 댓글이 달린 끝에 결정이 내려졌고 이제 기능이 안정화되었습니다. 이것은 impl Trait 대한 여전히 불안정한 사용 사례를 추적하기 위해 열려 있는 기능의 추적 문제입니다. impl Trait 의 해결된 측면을 다시 다루는 것은 이 추적 문제의 주제에서 벗어났습니다. 이 문제에 대해 계속 이야기하는 것은 환영하지만 이슈 트래커에서는 하지 마세요.

impl Trait 이 트레이트의 fns에 대한 인수 위치에서도 지원을 받지 못했는데 어떻게 안정화 되었나요?

@daboross 그럼 원본 포스트의 체크박스를 체크해야 합니다!

(https://play.rust-lang.org/?gist=47b1c3a3bf61f33d4acb3634e5a68388&version=stable이 현재 작동한다는 것을 발견했습니다)

https://play.rust-lang.org/?gist=c29e80715ac161c6dc95f96a7f91aa8c&version=stable&mode=debug 가 (아직) 작동하지 않는 것이 이상하다고 생각합니다. 이 오류 메시지가 더 중요합니다. 이렇게 생각하는건 나뿐인가? 트레이트의 반환 위치에 impl Trait 에 대한 확인란을 추가해야 하거나 트레이트 기능에 대한 인수 위치에 impl Trait 만 허용하여 existential type 강제로 사용하도록 하는 것이 의식적인 결정이었을 수도 있습니다. 반환 유형의 경우

@Ekleog

특성 함수에 대한 인수 위치에 impl Trait만 허용하여 반환 유형에 대해 실존 유형을 강제로 사용하도록 한 것이 의식적인 결정이었습니까?

예-- 트레잇에서 실존적 유형을 사용하는 더 실용적인 경험을 가질 때까지 트레잇에서 impl Trait 반환 위치가 연기되었습니다.

@cramertj 아직 구현하기에 충분한 실제 경험이 있습니까?

더 많은 기능을 추가하기 전에 몇 가지 안정적인 릴리스에서 impl Trait을 보고 싶습니다.

@mark-im 개인적으로 특성 메서드에 대한 반환 위치 impl Trait 에 대해 원격으로 논란의 여지가 있는 부분을 보지 못했습니다. 어쩌면 제가 놓치고 있는 부분이 있을 수 있습니다.

논란의 여지가 없다고 생각합니다. 기능을 너무 빨리 추가하고 있는 것 같습니다. 잠시 멈추고 기술적인 부채에 집중하고 현재 기능 세트에 대한 경험을 먼저 얻는 것이 좋을 것입니다.

내가 참조. 새로운 기능보다 기존 기능의 누락된 부분이라고 생각합니다.

@alexreg 가 옳다고 생각합니다. 특성 메소드에 존재하는 impl Trait 를 사용하는 것은 매우 유혹적입니다. 정말 새로운 기능은 아니지만 구현하기 전에 해결해야 할 몇 가지 사항이 있는 것 같습니다.

@phaazon 아마도, 예... 구현 세부 사항이 오늘날 우리가 이미 가지고 있는 것과 얼마나

특성에서 보편적인 임프 특성을 유지할 수 있는지 궁금합니다...

하지만 네, 당신의 요점을 알 것 같아요.

@mark-im 우리는 할 수 없습니다. 이미 안정적 입니다.

그것들은 함수에 있지만 Trait 선언은 어떻습니까?

스니펫에서 볼 수 있듯이 @mark-im은 impls 및 trait 선언 모두에서 안정적입니다..

abstract type 정하고 있는 곳을 따라잡기 위해 뛰어들기만 하면 됩니다. 개인적으로 저는 @Centril 의 최근 제안된 구문 및 모범 사례에

// GOOD:
type Foo: Iterator<Item: Display>;

type Bar = (impl Display, impl Debug);

// BAD
type Foo = impl Iterator<Item = impl Display>;

type Bar0: Display;
type Bar1: Debug;
type Bar = (Bar0, Bar1);

내 일부 코드에 적용되는 것은 다음과 같을 것입니다.

// Concrete type with a generic body
struct Data<TBody> {
    ts: Timestamp,
    body: TBody,
}


// A name for an inferred iterator
type IterData = Data<impl Read>;
type Iter: Iterator<Item = IterData>;


// A function that gives us an iterator. Also takes some arbitrary range
fn iter(&self, range: impl RangeBounds<Timestamp>) -> Result<Iter, Error> { ... }


// A struct that holds on to that iterator
struct HoldsIter {
    iter: Iter,
}

type Bar = (impl Display,); 는 좋지만 type Bar = impl Display; 는 나쁠 것입니다.

다른 대안적 존재 유형 구문(모두 rfc 2071 과 다름)을 결정하는 경우 https://users.rust-lang.org/ 의 포럼 스레드가 좋은 장소가 될까요?

나는 지금 그러한 스레드를 시작하기 위한 대안을 충분히 이해하지 못하고 있지만, 실존적 유형이 아직 구현되지 않았기 때문에 포럼에서 토론한 다음 새로운 RFC가 추적 문제에서 이에 대해 이야기하는 것보다 나을 것이라고 생각합니다. .

type Foo = impl Trait 무슨 문제가 있습니까?

@daboross 아마도 내부 포럼일 것입니다. 구문을 완성하기 위해 이에 대한 RFC를 작성하는 것을 고려하고 있습니다.

@daboross 이미 이 스레드에서 구문에 대한 충분한 논의가 있었습니다. @Centril 이 이 시점에서 RFC를 작성할 수 있다면

특성의 실존에 대한 토론을 위해 구독할 수 있는 문제가 있습니까?

한 구문 또는 다른 구문에 대한 매크로 관련 인수가 있습니까?

첫 번째 경우의 @tomaka type Foo = (impl Display,) 는 실제로 가지고 있는 유일한 구문입니다. 에 대한 나의 취향 type Foo: Trait 이상 type Foo = impl Trait 단지 우리가처럼, 우리가 이름을 지정할 수 있습니다 유형을 결합하고 있다는 사실에서 비롯 <TFoo: Trait> 또는 where TFoo: Trait 가진 반면, impl Trait 유형의 이름을 지정할 수 없습니다.

명확히 하자면, type Foo = impl Bar 가 나쁘다고 말하는 것이 아니라 부분적으로 @KodrAus 의 동기로 인해 간단한 경우에 type Foo: Bar더 낫다고 말하고 있습니다.

후자는 "Foo 유형이 Bar를 충족함"으로, 전자는 "Foo 유형이 Bar를 충족하는 일부 유형과 같음"으로 읽습니다. 따라서 전자는 확장적 관점("내가 Foo로 할 수 있는 일")에서 보다 직접 적이고 자연스럽습니다. 후자를 이해하려면 유형의 실존적 수량화에 대한 더 깊은 이해가 필요합니다.

type Foo: Bar 도 상당히 깔끔합니다. 왜냐하면 이것이 트레이트의 관련 유형에 대한 경계로 사용되는 구문인 경우 트레이트의 선언을 impl에 복사하기만 하면 간단히 작동하기 때문입니다. 노출하려는 모든 정보..).

구문은 특히 연관된 유형 경계가 관련되고 연관된 유형이 많은 경우 더 간결합니다. 이것은 노이즈를 줄여 가독성을 높일 수 있습니다.

@KodrAus

이러한 유형 정의를 읽는 방법은 다음과 같습니다.

  • type Foo: Trait 는 " FooTrait 구현하는 유형"을 의미합니다.
  • type Foo = impl Trait 는 " FooTrait 구현하는 일부 유형의 별칭입니다"를 의미합니다.

나에게 Foo: TraitFoo 구현 Trait Foo 에 대한 제약 조건을 선언합니다. 어떤 면에서는 type Foo: Trait 불완전하게 Foo 의 실제 정의가 없습니다.

반면에 impl Trait 는 "이것은 단일 유형이지만 컴파일러가 그 이름을 알아냅니다"를 연상시킵니다. 따라서 type Foo = impl Trait 는 이미 구체적인 유형( Trait 을 구현함)이 있음을 의미하며, 그 중 Foo 는 단지 별칭입니다.

type Foo = impl Trait 이 올바른 의미를 더 명확하게 전달한다고 생각합니다. FooTrait 구현하는 일부 유형의 별칭입니다.

@stjepang

type Foo: Trait 는 "Foo는 특성을 구현하는 유형입니다"를 의미합니다.
[..]
어떤 면에서는 type Foo: Trait 가 불완전하게 느껴집니다.

이것은 내가 읽는 방법이기도 하며(모듈로 표현...) 확장적으로 올바른 해석입니다. 이것은 Foo (유형이 제공하는 모피즘)으로 할 수 있는 모든

반면에 impl Trait 는 "이것은 단일 유형이지만 컴파일러가 그 공백을 채운다"는 것을 연상시킵니다. 따라서 type Foo = impl TraitFoo 가 별칭인 구체적인 유형( Trait 구현)을 이미 가지고 있음을 의미하지만 컴파일러는 그것이 실제로 어떤 유형인지 알아낼 것입니다.

이것은 확장적 관점에서 볼 때 중복되는 표현과 관련된 보다 상세하고 내포적인 해석입니다. 그러나 이것은 내포적인 의미에서 더 완전합니다.

@센트릴

독자, 특히 초보자의 입장에서는 확장성이 더 중요하다고 생각합니다.

이것은 확장적 관점에서 볼 때 중복되는 표현과 관련된 보다 자세하고 의도적인 해석입니다.

확장 대 내포 이분법은 흥미롭습니다. 저는 impl Trait 이런 식으로 생각해 본 적이 없습니다.

그래도 결론에 대해서는 의견이 갈립니다. FWIW, 저는 Haskell과 Scala에서 실존적 유형을 다루지 못했으므로 저를 초보자로 간주하십시오. :) Rust의 impl Trait 는 첫날부터 매우 직관적으로 느껴졌습니다. 아마도 제가 그것을 유형으로 할 수 있는 것보다 제한된 별칭으로 생각하기 때문일 것입니다. 따라서 Foo 무엇인지 아는 것과 이를 사용 하여 수행할 수 있는 작업 사이에서 전자를 선택합니다.

내 2c지만. 다른 사람들은 impl Trait 의 다른 멘탈 모델을 가질 수 있습니다.

나는 이 의견에 전적으로 동의합니다. type Foo: Trait 은(는) 불완전한 느낌입니다. 그리고 type Foo = impl Trait 는 다른 곳에서 impl Trait 를 사용하는 것과 더 유사하게 느껴지므로 언어가 더 일관되고 기억에 남도록 도와줍니다.

@joshtriplett 일관성 논의를 시작 하려면 https://github.com/rust-lang/rust/issues/34511#issuecomment -387238653을 참조 type Foo: Trait 허용하는 것은 https://github.com/rust-lang/rfcs/pull/2289 와도 특히 잘 맞습니다. type Foo: Iterator<Item: Display>; 를 사용하면 깔끔하게 균일하게 만듭니다.

@stjepang type Foo: Bar; 의 확장적 관점에서는 유형 이론의 실존적 수량화를 이해할 필요가 없습니다. 정말 이해해야 할 것은 FooBar 제공하는 모든 작업을 수행할 수 있다는 것입니다. Foo 의 사용자 관점에서 볼 때 흥미로운 부분이기도 합니다.

@센트릴

나는 당신이 어디에서 왔는지와 Type: Trait 구문을 가능한 한 많은 곳으로 밀어 넣는 매력을 이제 이해했다고 믿습니다.

: 는 type-implements-trait bounds에 사용되고 = 는 type 정의와 type-equals-another-type bounds에 사용된다는 강한 의미가 있습니다.

이것은 RFC에서도 분명하다고 생각합니다. 예를 들어 다음 두 가지 유형 경계를 사용합니다.

  • Foo: Iterator<Item: Bar>
  • Foo: Iterator<Item = impl Bar>

결국 이 두 경계는 동일한 효과를 갖지만 (내 생각에) 미묘하게 다릅니다. 전자는 " ItemBar 구현해야 합니다 "라고 말하고 후자는 " ItemBar 구현하는 어떤 유형과 같아야 합니다 "라고 말합니다.

다른 예를 사용하여 이 아이디어를 설명하겠습니다.

trait Person {
    type Name: Into<String>; // Just a type bound, not a definition!
    // ...
}

struct Alice;

impl Person for Alice {
    type Name = impl Into<String>; // A concrete type definition.
    // ...
}

그렇다면 Person 를 구현하는 실존형을 어떻게 정의해야 할까요?

  • type Someone: Person , 유형 바운드처럼 보입니다.
  • type Someone = impl Person , 유형 정의처럼 보입니다.

@stjepang 타입 바운드처럼 보이는 것은 나쁜 것이 아닙니다 :) 우리는 Person for Alice 과 같이 구현할 수 있습니다:

struct Alice;
trait Person          { type Name: Into<String>; ... }
impl Person for Alice { type Name: Into<String>; ... }

봐봐! { .. } 안의 내용은 특성과 impl 모두 동일합니다. 즉, Name 에 관한 한 그대로 특성에서 텍스트를 복사할 수 있습니다.

연관된 유형은 유형 수준 함수(여기서 첫 번째 인수는 Self )이므로 유형 별칭을 0-arity 연관 유형으로 볼 수 있으므로 이상한 일이 발생하지 않습니다.

결국 이 두 경계는 동일한 효과를 갖지만 (내 생각에) 미묘하게 다릅니다. 전자는 "항목은 특성 막대를 구현해야 함"이라고 말하고 후자는 "항목은 막대를 구현하는 일부 유형과 같아야 합니다"라고 말합니다.

네; 나는 첫 번째 표현이 더 요점에 가깝고 자연스럽다고 생각합니다. :)

@센트릴 ㅎ. 그것은 type Thing; 만으로도 추상 유형을 도입하기에 충분하다는 것을 의미합니까?

trait Neg           { type Output; fn neg(self) -> Self::Output; }
impl Neg for MyType { type Output; fn neg(self) -> Self::Output { self } }

@kennytm 기술적으로 가능하다고 생각합니다. 그러나 암시적/명시적에 대한 생각에 따라 바람직한지 아닌지를 물을 수 있습니다. 그 특정한 경우에 기술적으로 다음과 같이 작성하는 것으로 충분하다고 생각합니다.

trait Neg           { type Output; fn neg(self) -> Self::Output; }
impl Neg for MyType { fn neg(self) -> Self::Output { self } }

컴파일러는 type Output: Sized; 를 추론할 수 있습니다(이는 정보를 제공하지 않는 매우 흥미롭지 않은 범위입니다). 더 흥미 경계를 위해 고려해야 할 무언가이다,하지만 구체적인 유형으로 인해 프로그래머의 게으름에, 매우 간단하지 : 나도 것입니다 경우에도, 그것은 낮은 어포던스 API를 장려 할 수 생각하기 때문에 내 초기 제안에되지 않습니다 type Output; 는 처음에 같은 이유로 있습니다.

이 모든 것을 읽은 후에 @Centril에 더 동의하는 경향이 있다고 생각합니다. type Foo = impl Bar 볼 때 다른 별칭과 마찬가지로 Foo 가 특정 유형이라고 생각하는 경향이 있습니다. 하지만 그렇지 않습니다. 다음 예를 고려하십시오.

type Displayable = impl Display;

fn foo() -> Displayable { "hi" }
fn bar() -> Displayable { 42 }

IMHO 그것은 Displayable 선언에서 =를 보는 것이 약간 이상하지만 foo와 bar의 반환 유형이 동일하지 않습니다(즉, this =는 다른 곳과 달리 전이적이지 않습니다). 문제는 Foo가 어떤 Trait를 암시하는 특정 유형의 별칭이 _아닙니다_는 것입니다. 다시 말해, 어떤 컨텍스트에서 사용하든 단일 유형이지만 해당 유형은 예제와 같이 용도에 따라 다를 수 있습니다.

몇몇 사람들은 type Foo: Bar 이 "불완전한" 느낌이 든다고 언급했습니다. 나에게 이것은 좋은 일이다. 어떤 의미에서 Foo 는 불완전합니다. 우리는 그것이 무엇인지 모르지만 Bar 만족한다는 것을 압니다.

@마크임

문제는 Foo가 일부 Trait을 암시하는 특정 유형의 별칭이 아니라는 것입니다. 다시 말해, 어떤 컨텍스트에서 사용하든 단일 유형이지만 해당 유형은 예제와 같이 용도에 따라 다를 수 있습니다.

와, 정말 사실인가요? 그것은 확실히 나에게 매우 혼란 스러울 것입니다.

Displayable 가 단일 구체적인 유형이 아니라 impl Display 의 약칭인 이유가 있습니까? 특성 별칭(추적 문제: https://github.com/rust-lang/rust/issues/41517)을 비슷한 방식으로 사용할 수 있다는 점을 고려하면 이러한 동작이 유용한가요? 예시:

trait Displayable = Display;

fn foo() -> impl Displayable { "hi" }
fn bar() -> impl Displayable { 42 }

@마크임

type Displayable = impl Display;

fn foo() -> Displayable { "hi" }
fn bar() -> Displayable { 42 }

그것은 유효한 예가 아닙니다. RFC 2071의 실존 유형에 대한 참조 섹션에서 :

existential type Foo = impl Debug;

Foo 는 모듈 전체의 여러 위치에서 i32 로 사용할 수 있습니다. 그러나, 각각의 기능은 사용하는 Foo 로서 i32 해야시 독립적 장소 제약 Foo 이 있어야되도록 i32

각 존재 유형 선언은 최소한 하나의 함수 본문 또는 const/static 이니셜라이저에 의해 제한되어야 합니다. 본문 또는 이니셜라이저는 주어진 존재 유형에 대해 완전히 제한하거나 제한을 두지 않아야 합니다.

직접 언급되지는 않았지만 RFC의 나머지 부분이 작동하는 데 필요한 것은 존재 유형의 범위에 있는 두 함수가 해당 존재에 대해 다른 구체적인 유형을 결정할 수 없다는 것입니다. 그것은 일종의 충돌하는 유형 오류가 될 것입니다.

fooDisplayable 의 구체적인 유형을 이미 foo 설정했기 때문에 귀하의 예는 bar 반환 시 expected type `&'static str` but found type `i32` 와 같은 것을 줄 것이라고 생각합니다 &'static str .

편집: 당신이 직관에서 이것에 오지 않는 한

type Displayable = impl Display;

fn foo() -> Displayable { "hi" }
fn bar() -> Displayable { 42 }

와 동등하다

fn foo() -> impl Display { "hi" }
fn bar() -> impl Display { 42 }

내 기대보다는

existential type _0 = impl Display;
type Displayable = _0;

fn foo() -> Displayable { "hi" }
fn bar() -> Displayable { 42 }

이 두 가지 해석 중 어느 것이 정확한지는 @Centril 이 작성할 수 있는 RFC에 따라 달라질 수 있습니다.

문제는 Foo가 일부 Trait을 암시하는 특정 유형의 별칭이 아니라는 것입니다.

이 두 가지 해석 중 어느 것이 정확한지는 @Centril 이 작성할 수 있는 RFC에 따라 달라질 수 있습니다.

type Displayable = impl Display; 가 존재하는 이유는 특정 유형의 별칭이기 때문입니다.
이 기능이 해결하는 문제인 https://github.com/rust-lang/rfcs/issues/1738 을 참조

@Nemo157 예상이 맞습니다. :)

다음과 같은:

type Foo = (impl Bar, impl Bar);
type Baz = impl Bar;

다음과 같이 제거됩니다.

/* existential */ type _0: Bar;
/* existential */ type _1: Bar;
type Foo = (_0, _1);

/* existential */ type _2: Bar;
type Baz = _2;

여기서 _0 , _1_2 는 모두 명목상 다른 유형이므로 Id<_0, _2> Id<_0, _1> , Id<_0, _2> , Id<_1, _2> (및 대칭 인스턴스)는 모두 무인도이며 Idrefl 에 정의되어 있습니다.

면책 조항: 나는 (자발적으로) RFC를 읽지 않았습니다(그러나 그것이 무엇에 관한 것인지 알고 있습니다). 그래서 구문으로 "직관적"으로 느껴지는 것에 대해 논평할 수 있습니다.

type Foo: Trait 구문의 경우 다음과 같은 것이 가능할 것으로 완전히 예상합니다.

trait Trait {
    type Foo: Display;
    type Foo: Debug;
}

where Foo: Display, Foo: Debug 와 같은 방식으로 현재 가능합니다.

허용되지 않는 구문이라면 구문에 문제가 있다고 생각합니다.

아, 그리고 제 생각에는 Rust의 문법이 많을수록 배우기가 더 어려워집니다. 한 구문이 "배우기 더 쉽다"고 해도 두 구문이 필요한 한 초보자는 결국 두 구문을 모두 배워야 하고 이미 존재하는 프로젝트에 참여하는 경우 머지 않아 더 빨리 배워야 할 것입니다.

@Ekleog

type Foo: Trait 구문의 경우 다음과 같은 것이 가능할 것으로 완전히 예상합니다.

것이 가능하다. 이러한 "유형 별칭"은 관련 유형을 선언합니다(유형 별칭은 0-ary 유형 수준 함수로 해석될 수 있는 반면 관련 유형은 1+-ary 유형 수준 함수임). 물론 하나의 특성에 동일한 이름을 가진 여러 유형을 연결할 수는 없습니다. 이는 모듈에서 동일한 이름을 가진 두 개의 유형 별칭을 정의하려고 하는 것과 같습니다. impl 에서 type Foo: Bar 도 실존 수량화에 해당합니다.

아, 그리고 제 생각에는 Rust의 문법이 많을수록 배우기가 더 어려워집니다.

두 구문 모두 이미 사용 중입니다. type Foo: Bar; 는 이미 특성에서 합법적이며 Foo 가 유형 변수인 Foo: Bar 와 같은 보편적 수량화에도 적합합니다. impl Trait 는 반환 위치의 실존 수량화 및 인수 위치의 보편적 수량화에 사용됩니다. 두 플러그 모두 언어의 일관성 격차를 허용합니다. 또한 서로 다른 시나리오에 최적이므로 둘 모두를 사용하면 전역 최적값을 얻을 수 있습니다.

게다가, 초보자는 type Foo = (impl Bar, impl Baz); 가 필요하지 않을 것입니다. 대부분의 용도는 type Foo: Bar; 입니다.

RFC 2071에 대한 원래 pull 요청에는 이 토론에서 완전히 무시된 것으로 보이는 typeof 키워드가 언급되어 있습니다. 현재 제안된 구문은 컴파일러와 코드를 읽는 사람이 모두 구체적인 유형을 검색하도록 하는 다소 암시적이라는 것을 알았습니다.

이것이 명시적으로 밝혀지면 좋겠습니다. 그래서 대신

type Foo = impl SomeTrait;
fn foo_func() -> Foo { ... }

우리는 쓸 것이다

fn foo_func() -> impl SomeTrait { ... }
type Foo = return_type_of(foo_func);

(경륜할 return_type_of의 이름으로), 또는 심지어

fn foo_func() -> impl SomeTrait as Foo { ... }

새로운 키워드가 필요하지 않으며 impl Trait 구문을 아는 사람이라면 누구나 쉽게 이해할 수 있습니다. 후자의 구문은 간결하고 모든 정보가 한 곳에 있습니다. 특성의 경우 다음과 같이 보일 수 있습니다.

trait Bar
{
    type Assoc: SomeTrait;
    fn func() -> Assoc;
}

impl Bar for SomeType
{
    type Assoc = return_type_of(Self::func);
    fn func() -> Assoc { ... }
}

또는

impl Bar for SomeType
{
    fn func() -> impl SomeTrait as Self::Assoc { ... }
}

이것이 이미 논의되고 기각되었다면 죄송하지만 찾을 수 없었습니다.

@센트릴

것이 가능하다. 이러한 "유형 별칭"은 관련 유형을 선언합니다(유형 별칭은 0-ary 유형 수준 함수로 해석될 수 있는 반면 관련 유형은 1+-ary 유형 수준 함수임). 물론 하나의 특성에 동일한 이름을 가진 여러 유형을 연결할 수는 없습니다. 이는 모듈에서 동일한 이름을 가진 두 개의 유형 별칭을 정의하려고 하는 것과 같습니다. impl에서 유형 Foo: Bar는 실존 수량화에도 해당합니다.

(죄송합니다, 나는에 넣어 의미 impl Trait for Struct ,하지에 trait Trait )

죄송합니다. 제가 이해했는지 잘 모르겠습니다. 내가 말하려고 하는 것은 나에게 다음과 같은 코드를 작성한다는 것입니다.

impl Trait for Struct {
    type Type: Debug;
    type Type: Display;

    fn foo() -> Self::Type { 42 }
}

(풀버전 플레이그라운드 링크)
작동해야 하는 것처럼 느껴집니다 .

where Type: Debug, Type: Display work 와 같은 방식으로 where Type: Debug, Type: Display Type 에 두 개의 경계를 설정하기 때문입니다.

이것은 내가 "물론 당신은 하나의 특성에 같은 이름을 가진 여러 개의 관련 유형을 가질 수 없습니다"로 이해하는 것?하지만 서면으로 내 오류를 부여 무엇을 (안 허용하는 경우 trait Trait 대신 impl Trait for Struct 확실하지 않음), type Type: Trait 구문에 문제가 있는 것 같습니다.

그런 다음 trait 선언 내에서 구문은 이미 type Type: Trait 이며 다중 정의를 허용하지 않습니다. 그래서 아마도 이 배는 이미 오래전에 항해한 것 같은데…

그러나 위에서 @stjepang@joshtriplett 가 지적했듯이 type Type: Trait 는 불완전한 느낌입니다. 그리고 trait 선언에서는 의미가 있을 수 있지만(여러 정의를 허용하지 않는 것이 이상하더라도 실제로 불완전 하도록 설계됨 ) impl Trait 블록에서는 의미가 없습니다. , 여기서 유형은 확실히 알려 져야 합니다(현재 type Type = RealType 로만 작성할 수 있음).

impl Trait 는 반환 위치의 실존 수량화 및 인수 위치의 보편적 수량화에 사용됩니다.

네, 저도 이 글을 쓸 때 impl Trait 인수 위치에 대해 생각했는데, 안정화 단계에 있다는 것을 알았다면 인수 위치에서 impl Trait 에 대해 같은 주장을 지지했다고 해야 할까요? . 즉, 이 논쟁에 다시 불을 붙이지 않는 것이 낫다고 생각합니다. :)

두 플러그 모두 언어의 일관성 격차를 허용합니다. 또한 서로 다른 시나리오에 최적이므로 둘 모두를 사용하면 전역 최적값을 얻을 수 있습니다.

최적의 단순함

글쎄, 나는 때때로 단순함을 위해 최적의 것을 잃는 것이 좋은 것이라고 생각합니다. 마찬가지로 C와 ML은 거의 같은 시기에 태어났습니다. C는 단순성을 위해 최적에 양보를 했고, ML은 최적에 훨씬 가까웠지만 훨씬 더 복잡했습니다. 이러한 언어의 파생어를 세어도 C 개발자와 ML 개발자의 수는 비교할 수 없다고 생각합니다.

impl Trait:

현재 impl Trait: 구문을 중심으로 동일한 기능 세트에 대해 두 가지 대체 구문을 모두 만드는 경향이 있는 것 같습니다. 그러나 동일한 기능에 대해 두 가지 구문을 사용하는 것은 사용자를 혼란스럽게 할 수 있으므로, 특히 정확한 의미가 항상 미묘하게 다를 때 사용자를 혼란스럽게 할 수 있기 때문에 그것이 좋은 것이라고 생각하지 않습니다.

type Type: Trait 가 처음 type Type = impl Trait 오는 것을 항상 본 초보자를 상상해 보십시오. 그들은 무슨 일이 일어나고 있는지 짐작할 수 있지만, “WTF가 저게 그거야? 나는 몇 년 동안 Rust를 사용해 왔지만 아직 본 적이 없는 구문이 있습니까?”. 어느 정도 C++이 빠진 함정입니다.

기능 팽창

기본적으로 기능이 많을수록 언어를 배우는 것이 더 어렵다고 생각합니다. 그리고 type Type: Trait 보다 type Type = impl Trait type Type: Trait 를 사용하는 것이 큰 이점은 없다고 생각합니다.

rustc 를 사용하는 사람이 type Type = impl Trait 를 사용하도록 작성하는 type Type: Trait 을 볼 때 오류를 출력하도록 하는 것이 나에게 훨씬 더 의미가 있습니다. 적어도 하나의 작성 방법이 있습니다. , 그것은 모두에게 의미가 있으며( impl Trait 는 이미 반환 위치에서 존재하는 것으로 명확하게 인식됨) 모든 사용 사례를 다룹니다. 그리고 사람들이 직관적이라고 생각하는 것을 사용하려고 하면(나는 그것에 동의하지 않지만, 나에게는 = impl Trait 가 현재 = i32 비해 더 직관적임), 그들은 올바르게 리디렉션됩니다. 그것을 쓰는 전통적인 방법.

RFC 2071에 대한 원래 pull 요청에는 이 토론에서 완전히 무시된 것으로 보이는 typeof 키워드가 언급되어 있습니다. 현재 제안된 구문은 컴파일러와 코드를 읽는 사람이 모두 구체적인 유형을 검색하도록 하는 다소 암시적이라는 것을 알았습니다.

typeof 는 1.5년 전에 내가 열었던 문제에서 간략하게 논의되었습니다: https://github.com/rust-lang/rfcs/issues/1738#issuecomment -258353755

초보자로서 말하면 type Foo: Bar 구문이 혼란스럽습니다. 연관된 유형 구문이지만, 이는 구조체가 아니라 특성에 있어야 합니다. impl Trait 한 번 표시되면 그것이 무엇인지 알 수 있고 그렇지 않으면 찾을 수 있습니다. 다른 구문으로 그렇게 하는 것이 더 어렵고 이점이 무엇인지 잘 모르겠습니다.

언어 팀의 일부 사람들은 실제로 impl Trait 를 사용하여 실존 유형의 이름을 지정하는 것을 반대하므로 대신 다른 것을 사용하는 것 같습니다. 여기 댓글 도 저에게는 별로 의미가 없습니다.

하지만 어쨌든 이 말은 죽도록 두들겨 맞았던 것 같아요. 구문에 대한 수백 개의 의견과 소수의 제안만 있을 수 있습니다(나는 상황을 악화시킬 뿐이라는 것을 알고 있습니다). 어떤 구문도 모든 사람을 행복하게 만들 수는 없으며 모든 구문에 대해 찬성과 반대의 주장이 있습니다. 어쩌면 우리는 하나를 선택하고 그것에 충실해야합니다.

와우, 전혀 이해하지 못했습니다. 저를 바로 잡아주신 @Nemo157님 감사합니다!

이 경우 = 구문을 선호합니다.

@Ekleog

type Type: Trait 구문에 문제가 있다고 생각합니다.

그것은 허용 될 있으며 그것은 완벽하게 정의 될 수 있지만 일반적으로 쓰기 where Type: Foo + Bar 대신 where Type: Foo, Type: Bar 그건 아주 좋은 생각은 아닌 것 같아, 그래서. 연관된 유형의 경우 대신 Foo + Bar 를 작성하도록 제안하는 이 경우에 대해 좋은 오류 메시지를 쉽게 실행할 수도 있습니다.

type Foo = impl Bar; 또한 당신이 볼 점에서 이해 가능성의 문제가 = impl Bar 하고 그냥 같이 사용되는 각 발생에 그것을 대체 할 수 있다는 결론 -> impl Bar ; 그러나 그것은 작동하지 않을 것입니다. @mark-im이 이 해석을 내놓았는데, 이는 실수할 가능성이 훨씬 더 높아 보입니다. 따라서 type Foo: Bar; 가 학습 가능성을 위한 더 나은 선택이라는 결론을 내립니다.

그러나 위에서 @stjepang@joshtriplett 가 지적한 것처럼 Type: Trait 를 입력하면 불완전한 느낌이 듭니다.

확장 POV에서 불완전하지 않습니다. 당신은에서 많은 정보로 정확하게 얻을 type Foo: Bar; 당신이에서 얻을으로 type Foo = impl Bar; . 따라서 type Foo: Bar; 할 수 있는 일 의 관점에서 보면 완료되었습니다. 실제로 후자는 type _0: Bar; type Foo = _0; 로 디슈가 처리됩니다.

편집: 내가 의미하는 바는 그것이 일부에게는 불완전하게 느껴질 수 있지만 기술적인 관점에서는 그렇지 않다는 것입니다.

즉, 이 논쟁에 다시 불을 붙이지 않는 것이 낫다고 생각합니다. :)

좋은 생각이야. 우리는 디자인할 때 우리가 원했던 것이 아니라 있는 그대로의 언어를 고려해야 합니다.

글쎄, 나는 때때로 단순함을 위해 최적의 것을 잃는 것이 좋은 것이라고 생각합니다.

단순함을 추구해야 한다면 대신 type Foo = impl Bar; 를 삭제하겠습니다.
C의 단순성(Haskell Core 및 이와 유사한 것들이 여전히 건전하면서도 더 간단할 수 있기 때문에 가정합니다..)은 표현력과 건전함과 관련하여 엄청난 대가를 치르게 된다는 점에 유의해야 합니다. C는 언어 디자인에서 제 별점이 아닙니다. 그것에서 멀리.

현재 impl Trait: 구문을 중심으로 동일한 기능 세트에 대해 두 가지 대체 구문을 모두 만드는 경향이 있는 것 같습니다. 그러나 동일한 기능에 대해 두 가지 구문을 사용하는 것은 사용자를 혼란스럽게 할 수 있으므로, 특히 정확한 의미가 항상 미묘하게 다를 때 사용자를 혼란스럽게 할 수 있기 때문에 그것이 좋은 것이라고 생각하지 않습니다.

그러나 그들은 자신의 의미에 전혀 차이가 없습니다. 하나는 다른 하나에 설탕을 제거합니다.
type Foo: Bar; 또는 type Foo = impl Bar 둘 중 하나만 완벽하게 정의된 의미 체계를 가지고 있음에도 불구하고 작동하지 않는 것에 대한 혼란은 사용자에게만 방해가 된다고 생각합니다. 사용자가 type Foo = impl Bar; 를 작성하려고 하면 Lint가 실행되고 type Foo: Bar; 제안합니다. 린트가 사용자에게 다른 구문에 대해 가르치고 있습니다.
저에게는 언어가 획일적이고 일관성이 있다는 것이 중요합니다. 어딘가에서 두 구문을 모두 사용하기로 결정했다면 그 결정을 일관되게 적용해야 합니다.

type Type: Trait 가 처음 type Type = impl Trait 오는 것을 항상 본 초보자를 상상해 보십시오.

이 특정 경우에 린트가 실행되고 이전 구문을 권장합니다. type Foo = (impl Bar, impl Baz); 에 관해서는 초보자는 어쨌든 -> impl Trait 을 배워야 하므로 그 의미를 유추할 수 있어야 합니다.

어느 정도 C++이 빠진 함정입니다.

C++의 문제는 주로 그것이 꽤 오래되었고, C의 짐이 있고, 너무 많은 패러다임을 지원하는 많은 기능이 있다는 것입니다. 이는 별개의 기능이 아니라 구문이 다를 뿐입니다.

기본적으로 기능이 많을수록 언어를 배우는 것이 더 어렵다고 생각합니다.

새로운 언어를 배우는 것은 주로 중요한 라이브러리를 배우는 것이라고 생각합니다. 그것은 대부분의 시간을 보낼 곳입니다. 올바른 기능은 라이브러리를 훨씬 더 구성 가능하게 만들고 더 많은 경우에 작동할 수 있습니다. 나는 낮은 수준의 생각을 강요하고 중복을 유발하는 언어보다 좋은 추상력을 제공하는 언어를 훨씬 선호합니다. 이 경우 우리는 추상적인 힘을 더하거나 심지어 기능조차 추가하지 않고 더 나은 인체 공학을 추가합니다.

그리고 type Type: Trait 보다 type Type = impl Trait: type Type: Trait 를 사용하는 것이 큰 이점은 없다고 생각합니다. 예를 들어 6개의 문자가 저장되나요?

예, 6자만 저장되었습니다. 그러나 type Foo: Iterator<Item: Iterator<Item: Display>>; 고려하면 대신 훨씬 더 많은 노이즈가 있는 type Foo = impl Iterator<Item = impl Iterator<Item = impl Display>>; 를 얻게 됩니다. type Foo: Bar; 는 후자에 비해 더 직접적이며 잘못 해석(재대체..)할 가능성이 적고 연관된 유형에 더 잘 작동합니다(특성에서 유형 복사..).
또한 type Foo: Bartype Foo: Bar = ConcreteType; 로 자연스럽게 확장될 수 있으며 이는 구체적인 유형을 노출하지만 Bar 충족하도록 합니다. type Foo = impl Trait; 대해 이러한 작업을 수행할 수 없습니다.

Rustc가 type Type: Trait 를 볼 때 오류를 출력하면 type Type = impl Trait 를 사용하도록 작성하는 사람이 말하는 것이 훨씬 더 이해가 됩니다. 적어도 하나의 작성 방법이 있습니다.

그들은 그것을 작성하는 전통적으로 올바른 방법으로 정당하게 리디렉션됩니다.

나는 무언가를 작성하는 한 가지 전통적인 방법이 있다고 제안합니다. type Foo: Bar; .

@lnicola

초보자로서 말하면 type Foo: Bar 구문이 혼란스럽습니다. 연관된 유형 구문이지만, 이는 구조체가 아니라 특성에 있어야 합니다.

유형 별칭은 실제로 연결된 유형으로 볼 수 있음을 반복합니다. 다음과 같이 말할 수 있습니다.

trait Foo        { type Baz: Quux; }
// User of `Bar::Baz` can conclude `Quux` but nothing more!
impl Foo for Bar { type Baz: Quux; }

// User of `Wibble` can conclude `Quux` but nothing more!
type Wibble: Quux;

연결된 유형 및 유형 별칭에서 정확히 동일하게 작동함을 알 수 있습니다.

예, 6자만 저장되었습니다. 그러나 type Foo: Iterator<Item: Iterator<Item: Display>>; 고려하면 대신 다음을 얻을 수 있습니다. type Foo = impl Iterator<Item = impl Iterator<Item = impl Display>> ; 훨씬 더 많은 소음이 있습니다.

이것은 명명된 실존을 선언하는 구문과 직교하는 것처럼 보입니다. 제안된 것으로 기억하는 네 가지 구문은 모두 다음과 같이 잠재적으로 허용합니다.

type Foo: Iterator<Item: Iterator<Item: Display>>;
type Foo = impl Iterator<Item: Iterator<Item: Display>>;
existential type Foo: Iterator<Item: Iterator<Item: Display>>;
existential type Foo = impl Iterator<Item: Iterator<Item: Display>>;

사용할 수 있다는 귀하의 속기 제안 Trait<AssociatedType: Bound> 대신 Trait<AssociatedType = impl Bound> 구문 실존 유형 (중 이름 또는 익명)의 관련 유형에 대한 익명의 존재 형식을 선언하는 독립적 인 기능 (그러나 아마 관련에 실존 유형 기능의 전체 집합을 일관성 있게 유지하는 조건).

@Nemo157 기능이 다릅니다. 하지만 일관성을 위해 함께 고려하는 것은 당연하다고 생각합니다.

@센트릴

미안하지만 그들은 틀렸다. 확장 POV에서 불완전하지 않습니다.

제안한 구문에 정보가 누락되었다고 제안한 적이 없습니다. 나는 그것이 불완전하게 느껴진다고 제안하고 있었다. 그것은 나에게, 그리고 다른 사람들에게 잘못 보인다. 나는 당신이 오고 있는 관점에서 당신이 그것에 동의하지 않는다는 것을 이해하지만 그것이 잘못된 느낌을 만들지는 않습니다.

또한 이 스레드에서 사람들이 이 정확한 구문 차이로 해석 문제를 시연했음을 관찰하십시오. type Foo = impl TraitFoo 가 다른 구체적인 유형을 취할 수 있는 특성에 대한 별칭이 아니라 몇 번을 사용해도 특정 하지만 이름이 없는 구체적인 유형이라는 것을 더 명확하게 하는 것 같습니다 Foo 사용할 때마다.

나는 그들이 그들에 대해 알고있는 모든 것들을 취할 수있는 사람들에게 도움이 생각 -> impl Trait 하고에 적용 type Foo = impl Trait ; 일반화된 개념 impl Trait 이 두 곳 모두에서 빌딩 블록으로 사용되는 것을 볼 수 있습니다. type Foo: Trait 와 같은 구문은 일반화된 빌딩 블록을 숨깁니다.

@joshtriplett

나는 그것이 불완전하게 느껴진다고 제안하고 있었다. 그것은 나에게, 그리고 다른 사람들에게 잘못 보인다.

괜찮은; 여기서 incomplete 와 다른 용어를 사용하는 것을 제안합니다. 왜냐하면 그것은 정보가 부족하다는 것을 암시하기 때문입니다.

또한 이 스레드에서 사람들이 이 정확한 구문 차이로 해석 문제를 시연했음을 관찰하십시오.

내가 관찰한 것은 스레드에서 type Foo = impl Bar; 의미에 대한 해석 실수였습니다. 한 사람은 Foo 의 다른 용도를 명목상 동일한 유형이 아니라 다른 유형으로 해석했습니다. 즉, 정확히 "사용할 때마다 다른 구체적인 유형을 취할 수 있는 특성의 별칭" 입니다.

어떤 사람들은 type Foo: Bar; 가 혼란스럽다고 말했지만 type Foo: Bar; 의 대체 해석이 의도한 의미와 다른 것이 무엇인지 잘 모르겠습니다. 대안적 해석에 대해 듣고 싶습니다.

@센트릴

유형 별칭은 실제로 연결된 유형으로 볼 수 있음을 반복합니다.

할 수 있지만 지금은 연관된 유형이 특성과 관련되어 있습니다. impl Trait 는 거의 모든 곳에서 작동합니다. impl Trait 를 일종의 연관 타입으로 제시하려면 한 번에 두 가지 개념을 도입해야 합니다. 즉, impl Trait 를 함수 반환 유형으로 보고 그 내용을 추측하거나 읽은 다음 유형 별칭에서 impl Trait 를 볼 때 해당 지식을 재사용할 수 있습니다.

특성 정의에서 연관된 유형을 보는 것과 비교하십시오. 이 경우 다른 구조체가 정의하거나 구현해야 하는 항목이라고 생각합니다. 그러나 특성 외부에서 type Foo: Debug 를 발견하면 그것이 무엇인지 알 수 없습니다. 구현하는 사람이 없으니 일종의 포워드 선언인가? C++에서와 같이 상속과 관련이 있습니까? 다른 사람이 유형을 선택하는 ML 모듈과 같습니까? 그리고 이전에 impl Trait 를 본 적이 있다면 이들 사이에 링크를 만들 필요가 없습니다. 우리는 쓰기 fn foo() -> impl ToString 하지 fn foo(): ToString .

유형 Foo = impl Bar; 또한 = impl Bar가 표시되고 -> impl Bar로 사용되는 경우마다 이를 대체할 수 있다는 결론을 내린다는 점에서 이해도 문제가 있습니다.

내가 전에 여기 말했다했지만, 사고 등의가 있다는 것을 let x = foo(); 당신이 사용할 수있는 수단 x 대신 foo() . 어쨌든 필요할 때 빠르게 찾아볼 수 있는 디테일이지만 근본적으로 개념을 바꾸지는 않는다.

즉, 작동 방식을 정확히 알지 못하더라도(상충되는 정의가 있을 때 어떤 일이 발생하는지) 이것이 무엇에 관한 것인지( -> impl Trait 와 같은 추론된 유형) 알아내는 것은 쉽습니다. 다른 구문을 사용하면 그것이 무엇인지조차 깨닫기가 어렵습니다.

@센트릴

괜찮은; 여기에서 불완전한 용어와 다른 용어를 사용할 것을 제안합니다. 왜냐하면 그것은 정보의 부족을 암시하기 때문입니다.

"불완전한"은 정보 의 부족을 의미할 필요가 없으며, 무언가가 다른 것을 갖고 있어야 하는 것처럼 보이지만 그렇지 않다는 것을 의미할 수 있습니다.

type Foo: Trait; 는 완전한 선언처럼 보이지 않습니다. 뭔가 빠진 것 같습니다. 그리고 그것은 type Foo = SomeType<X, Y, Z>; 와 전혀 다른 것처럼 보입니다.

아마도 우리는 단일 라이너만으로는 type Inferred: Traittype Inferred = impl Trait 사이의 이러한 합의 격차를 해소할 수 없다는 점에 도달하고 있습니다.

이 기능의 실험적 구현을 ​​모든 구문(RFC에 지정된 것 포함)과 함께 조합하여 더 큰 프로그램에서 이 기능이 컨텍스트에 맞는지 확인하기 위해 시작할 수 있도록 하는 것이 가치가 있다고 생각하십니까?

@lnicola

[..] impl Trait 는 어디에서나 작동하거나 거의

글쎄, Foo: Bound 또한 거의 모든 곳에서 작동합니다 ;)

그러나 특성 외부에서 type Foo: Debug 를 발견하면 그것이 무엇인지 알 수 없습니다.

특성 -> impl -> 유형 별칭에서 사용하는 과정이 학습에 도움이 된다고 생각합니다.
또한 "Foo가 디버그를 구현하는 유형"이라는 추론은 다음과 같습니다.
트레이트와 일반 범위에서 type Foo: Debug 를 보는 것도 정확합니다.

C++에서와 같이 상속과 관련이 있습니까?

Rust에서 상속의 부족은 우리가 논의하는 기능을 학습할 때보다 훨씬 더 이른 단계에서 학습해야 한다고 생각합니다. 이것이 Rust의 기본이기 때문입니다.

다른 사람이 유형을 선택하는 ML 모듈과 같습니까?

그 추론은 호출자(사용자)가 유형을 선택하는 arg: impl Bar 로 인해 type Foo = impl Bar; 대해서도 이루어질 수 있습니다. 나에게 사용자가 유형을 선택했다는 추론은 type Foo: Bar; 가능성이 낮아 보입니다.

내가 전에 여기 말했다했지만, 사고 등의가 있다는 것을 let x = foo(); 당신이 사용할 수있는 수단 x 대신 foo() .

언어가 참조적으로 투명한 경우 foo() 대신 x사용할 수 있습니다 . 시스템에 type Foo = impl Foo; 를 추가할 때까지 유형 별칭은 참조적으로 투명합니다. 반대로 x = foo() 바인딩이 이미 있는 경우 다른 foo()x 대체할 수 있습니다.

@joshtriplett

"불완전한"은 정보의 부족을 의미할 필요가 없으며, 무언가가 다른 것을 갖고 있어야 하는 것처럼 보이지만 그렇지 않다는 것을 의미할 수 있습니다.

그럴 수 있지; 그러나 그것이 가지고 있지 않은 것은 무엇입니까?

type Foo: Trait; 는 완전한 선언처럼 보이지 않습니다.

내가 보기에는 완전해 보인다. Foo 가 정확히 의도한 의미인 Trait 를 만족한다는 판단으로 보입니다.

@Centril 나에게 "뭔가 누락된 것"은 이것이 별칭인 실제 유형입니다. 그것은 조금 전에 내 혼란과 관련이 있습니다. 그런 유형이 없다는 것이 아니라 해당 유형이 익명이라는 것뿐입니다... =를 사용하는 것은 유형이 있다는 것을 암시하고 항상 동일한 유형이지만 이름을 지정할 수는 없습니다.

나는 우리가 이러한 주장을 소진하고 있다고 생각합니다. 두 구문을 모두 실험적으로 구현하고 무엇이 가장 잘 작동하는지 확인하는 것이 좋습니다.

@마크임

@Centril 나에게 "뭔가 누락된 것"은 이것이 별칭인 실제 유형입니다. 그것은 조금 전에 내 혼란과 관련이 있습니다. 그런 유형이 없다는 것이 아니라 해당 유형이 익명이라는 것뿐입니다... =를 사용하는 것은 유형이 있다는 것을 암시하고 항상 동일한 유형이지만 이름을 지정할 수는 없습니다.

저에게도 딱 그런 느낌입니다.

두 개의 연기된 항목을 곧 해결할 수 있는 가능성과 평생 생략에 대한 문제를 해결할 수 있습니까? 나는 그것을 스스로 할 것이지만 방법을 모른다!

impl Trait 정확히 무엇을 의미하는지에 대해서는 여전히 많은 혼란이 있으며, 전혀 명확하지 않습니다. 지연된 항목은 impl Trait (곧 제공될 예정임)의 정확한 의미에 대한 명확한 아이디어를 얻을 때까지 확실히 기다려야 한다고 생각합니다.

@varkor 어떤 의미가 불분명합니까? AFAIK impl Trait 의 의미 체계는 RFC 1951 이후 변경되지 않았으며 2071년에 확장되었습니다.

@alexreg 계획은 없었지만 대략적인 개요는 다음과 같습니다. 구문 분석이 추가된 후 기존 impl 내부에서 staticconst 의 유형을 줄여야 합니다. 여기에서 함수의 반환 유형에 대해 수행하는 것과 같은 특성 컨텍스트 DefId 에서 ImplTraitContext::Existential 그렇지 원하기 때문에, 선택을하여 impl Trait 부모 함수 정의에서 제네릭을 선택합니다. 그것은 당신에게 적절한 방법을 제공해야합니다. @oli-obk 의 실존 유형 PR 을 기반으로 하면 더 쉽게 시간을 보낼 수 있습니다.

@cramertj : 언어에서 impl Trait 의 의미는 완전히 함수 서명에서의 사용으로 제한되며 다른 위치로 확장하는 것이 분명한 의미가 있다는 것은 사실이 아닙니다. 대부분의 대화가 진행 중인 것으로 보이는 이 부분에 대해 곧 좀 더 자세히 말씀드리겠습니다.

@varkor

언어에서 impl Trait의 의미는 완전히 함수 서명에서의 사용으로 제한되며 다른 위치로 확장하는 것이 분명한 의미가 있다는 것은 사실이 아닙니다.

의미는 RFC 2071 에 명시되어 있습니다.

@cramertj : RFC 2071의 의미가 모호하며 "실존 유형"이라는 문구가 의미하는 바에 대한 여러 해석을 허용합니다.

TL;DR — impl Trait 대한 정확한 의미를 설정하려고 노력했습니다.

Rust의 존재 유형(포스트)


Discord Rust-lang 채팅에서 지난 며칠 동안 impl Trait 의 정확한 (즉, 형식적, 이론적) 의미 체계에 대해 많은 토론이 있었습니다. 기능에 대한 많은 세부 정보와 정확히 무엇이고 무엇이 아닌지를 명확히 하는 것이 도움이 되었다고 생각합니다. 또한 유형 별칭에 대해 어떤 구문이 그럴듯한지 설명합니다.

나는 우리의 결론 중 일부에 대한 약간의 요약을 썼습니다. 이것은의 해석 제공 impl Trait 나는 매우 깨끗 생각하고 정확하게 인수 위치 사이의 차이점에 대해 설명 impl Trait 와 리턴 위치를 impl Trait "universally-하지 않은 ( 정량화" vs "실존적으로 정량화"). 실용적인 결론도 있습니다.

여기에서 "실존하는 유형 별칭"의 일반적으로 명시된 요구 사항을 충족하는 새로운 구문을 제안합니다.
type Foo: Bar = _;

워낙 복잡한 주제이기 때문에 먼저 해명해야 할 부분이 많아 별도의 포스트로 작성하게 되었습니다. 피드백은 매우 감사합니다!

Rust의 존재 유형(포스트)

@varkor

RFC 2071은 모호하며 "실존 유형"이라는 문구가 의미하는 바에 대한 여러 해석을 허용합니다.

어떻게 모호합니까? 나는 당신의 게시물을 읽었습니다. 나는 여전히 정적 및 상수에서 비동적 실존의 한 가지 의미를 알고 있습니다. 항목당 새로운 존재 유형 정의를 도입하여 impl Trait 반환 위치와 동일한 방식으로 작동합니다.

type Foo: Bar = _;

우리는 RFC 2071에서 이 구문에 대해 논의했습니다. 거기에서 말했듯이 Foo 는 단일 유추 유형이며 현재 모듈 외부에 존재하는 유추되지 않은 유형에 대한 여지를 남겨 둡니다( 예: type Foo: Bar = u32; ). 나는 그것의 두 가지 측면이 마음에 들지 않았다. (1) 키워드가 없기 때문에 검색하기가 더 어렵다. (b) abstract type Foo: Bar; 구문과 비교하여 type Foo = impl Trait 와 비교하여 동일한 장황한 문제가 있다. type Foo = impl Iterator<Item = impl Display>;type Foo: Iterator<Item = MyDisplay> = _; type MyDisplay: Display = _; 됩니다. 나는 이들 중 어느 것도 거래 차단자가 아니라고 생각하지만 어떤 식으로든 IMO에서 확실한 승리는 아닙니다.

@cramertj 모호함이 여기에 나타납니다.

type Foo = impl Bar;
fn f() -> Foo { .. }
fn g() -> Foo { .. }

Foo 가 실제로 존재 유형에 대한 유형 별칭이었다면 fg다른 구체적인 반환 유형을 지원합니다. 몇몇 사람들은 본능적으로 이런 방식으로 구문을 읽었고 실제로 RFC 2071 구문 토론의 일부 참가자 는 최근 Discord 토론의 일부로 제안이 작동하는 방식이 아니라는 것을 깨달았습니다 .

문제는 특히 인수 위치 impl Trait 직면하여 존재 수량자가 어디로 가야 하는지 명확하지 않다는 것입니다. 인수의 경우 범위가 좁습니다. 반환 위치의 경우 범위가 좁은 것처럼 보이지만 그보다 넓은 것으로 판명되었습니다. type Foo = impl Bar 두 입장 모두 그럴듯합니다. _ 기반 구문은 이 문제를 깔끔하게 피하면서 "실존"도 포함하지 않는 해석으로 이동합니다.

경우 Foo 정말 실존 유형에 대한 유형의 별칭이었다

(강조 내). fg 가 동일한 존재 유형을 참조하기 때문에 다른 구체적인 반환 유형을 지원하지 _not_는 '특정'으로 'an'을 읽었습니다. 난 항상 본 type Foo = impl Bar; 같은 의미 사용으로 let foo: impl Bar; 즉, 새로운 익명의 존재 형식을 도입을; 귀하의 예를 다음과 동일하게 만들기

existential type _0: Bar;
type Foo = _0;
fn f() -> Foo { .. }
fn g() -> Foo { .. }

상대적으로 명확하기를 바랍니다.


한 가지 문제는 " impl Trait in type aliases" 의 의미가 RFC에 지정된 적이 없다는 것입니다. RFC 2071의 "대안" 섹션 에 간략하게 언급되어 있지만 이러한 고유한 교육 모호성 때문에 명시적으로 할인됩니다.

또한 유형 별칭이 이미 참조적으로 투명하지 않다는 언급을 본 것 같습니다. 나는 그것이 ur.rl.o에 있었던 것 같은데, 몇 가지 검색 후에 토론을 찾을 수 없었습니다.

@cramertj
@rpjohnst 의 요점에 이어 impl Trait 의 의미론에 대한 여러 해석이 있습니다. 모두 현재 서명의 사용법과 일치하지만 impl Trait 를 다른 것으로 확장할 때 다른 결과를 가집니다. 위치(게시물에 설명된 것 외에 2개를 알고 있지만 토론할 준비가 되지 않았습니다). 그리고 게시물의 해석이 반드시 가장 분명하다는 것이 사실이라고 생각하지 않습니다(개인적으로 그 관점에서 APIT 및 RTIP에 대한 유사한 설명을 보지 못했습니다).

type Foo: Bar = _; 에 관해서는 다시 논의해야 할 것 같습니다. 오래된 아이디어를 신선한 눈으로 다시 보는 것이 나쁠 것은 없습니다. 귀하의 문제에 대해:
(1) 키워드가 없지만 어디에서나 형식 유추와 동일한 구문입니다. "underscore" / "underscore type" / 등에 대한 문서를 검색하면 유형 추론에 대한 페이지를 쉽게 제공할 수 있습니다.
(2) 네, 맞습니다. 우리는 이에 대한 해결책을 생각해 왔으며, 밑줄 표기법과 잘 맞는다고 생각합니다. 곧 제안할 준비가 될 것입니다.

@cramertj 처럼 나는 여기서 논쟁을 실제로 보지 못합니다.

@varkor 의 게시물이 설명하는 근본적인 모호성을 볼 수 없습니다. 나는 우리가 언제나처럼 녹에 "존재 유형을"해석 한 생각 때문에 (들 '@varkor 같은 게시물을 말한다) "는 ... 적어도 하나 개의 유형이 존재한다"가 아니라 "...하는 _unique_ 유형이 존재한다" 후자는 "보편적 유형"과 동일하므로 "실존적 유형"이라는 문구는 해당 해석을 허용하려는 경우 완전히 쓸모가 없습니다. 주제에 대한 모든 RFC는 항상 보편적인 유형과 실존적 유형이 두 가지 별개의 것이라고 가정했습니다. 실제 유형 이론에서 그것이 의미하는 바이며 동형이 수학적으로 매우 현실적이라는 것을 알지만 나에게 그것은 우리가 유형 이론 용어를 오용하고 있으며 이에 대해 다른 전문 용어를 선택해야 한다는 주장일 뿐이지, impl Trait 의 의도된 의미는 항상 명확하지 않았으며 다시 생각해야 합니다.

@rpjohnst가 설명하는 범위 지정 모호성은 심각한 문제이지만 제안된 모든 구문은 유형 alises 또는 관련 유형과 잠재적으로 혼동될 수 있습니다. 그 혼란 중 "더 나쁨" 또는 "더 가능성이 있는" 것은 정확히 우리가 수백 개의 댓글 후에도 이미 해결하지 못한 끝없는 자전거 보관소입니다. 나는 type Foo: Bar = _; type Foo: Bar; 의 문제를 해결하는 것처럼 보이지만 약간 사소하지 않은 존재를 선언하기 위해 몇 가지 명령문이 폭발적으로 필요하다는 문제를 해결하는 것처럼 보이지만 실제로 그것을 바꾸기에는 충분하지 않다고 생각합니다. "영원한 자전거 보관소" 상황.

내가 확신하는 것은 "단지 type "구문은 너무 오해의 소지가 있기 때문에 우리가 끝내는 구문이 type 이외의 키워드를 가져야 한다는 것입니다. 사실, 구문에서 type 를 _전혀_ 사용하지 마십시오. 그래서 누군가가 "유형 별칭이지만 어떻게든 더 실존적"으로 보고 있다고 가정할 수 있는 방법이 없습니다.

existential Foo = impl Trait;
fn f() -> Foo { .. }
fn g() -> Foo { .. }
existential Foo: Trait;
fn f() -> Foo { .. }
fn g() -> Foo { .. }



md5-b59626c5715ed89e0a93d9158c9c2535



existential Foo: Trait = _;
fn f() -> Foo { .. }
fn g() -> Foo { .. }

fgTrait 구현하는 두 가지 다른 유형을 반환할 수 있다는 잘못된 해석을 완전히 _방지_하는 것은 나에게 분명하지 않지만 이것이 예방에 가깝다고 생각합니다. 우리는 아마도 얻을 수 있습니다.

@Ixrec
"실존적 유형"이라는 문구는 특히 범위가 모호하기 때문에 문제가 됩니다. APIT와 RPIT의 범위가 완전히 다르다는 것을 다른 사람이 지적하는 것을 본 적이 없습니다. 이는 impl Bar 가 "실존 유형"인 type Foo = impl Bar 와 같은 구문이 본질적으로 모호함을 의미합니다.

예, 유형 이론 용어가 많이 오용되었습니다. 그러나 RFC에서 오용(또는 최소한 설명되지 않음)되어 있으므로 RFC 자체에서 비롯된 모호성이 있습니다.

@rpjohnst가 설명하는 범위 지정 모호성은 심각한 문제이지만 제안된 모든 구문은 유형 alises 또는 관련 유형과 잠재적으로 혼동될 수 있습니다. 그 혼란 중 "더 나쁨" 또는 "더 가능성이 있는" 것은 정확히 우리가 수백 개의 댓글 후에도 이미 해결하지 못한 끝없는 자전거 보관소입니다.

아니오, 저는 이것이 사실이 아니라고 생각합니다. 이러한 혼동이 없는 일관된 구문을 생각해내는 것이 가능합니다. 나는 두 가지 현재 제안이 좋지 않아 아무도 만족하지 못하기 때문에 자전거를 버리는 것을 감행할 것입니다.

내가 확신하는 것은 우리가 끝내는 구문이 무엇이든 type 이외의 키워드가 필요하다는 것입니다.

이것도 필요없다고 생각합니다. 귀하의 예에서 완전히 새로운 표기법을 발명했습니다. 이는 가능한 한 언어 디자인에서 피하고 싶은 것입니다. 그렇지 않으면 일관성 없는 구문으로 가득 찬 거대한 언어를 생성하게 됩니다. 더 나은 옵션이 없을 때만 완전히 새로운 구문을 탐색해야 합니다. 그리고 저는 더 나은 선택이 _있다고 주장합니다.

제쳐두고: 참고로, 나는 "실존적 유형"에서 완전히 벗어나 전체 상황을 더 명확하게 만드는 것이 가능하다고 생각합니다. 이는 나 또는 다른 누군가가 곧 후속 조치를 취할 것입니다.

많은 사람들이 type 를 "매번 잠재적으로 다른 유형" 해석을 ​​의미하는 간단한 대체 가능한 별칭으로 해석하기 때문에 type 이외의 구문도 도움이 될 것이라고 생각합니다.

APIT와 RPIT의 범위가 완전히 다르다는 것을 다른 사람이 지적하는 것을 본 적이 없습니다.

범위 지정은 항상 impl Trait 제안의 명시적인 부분이므로 "지시"할 필요가 없다고 생각했습니다. 범위 지정에 대해 말씀하신 모든 내용은 과거 RFC에서 이미 수용한 내용을 반복하는 것 같습니다. 나는 그것이 구문에서 모든 사람에게 명확하지 않다는 것을 알고 그것이 문제이지만 아무도 이것을 이전에 이해하지 못한 것과 같습니다. 사실, RFC 2701에 대한 논의의 type Foo = impl Trait; 부분이

이러한 혼동이 없는 일관된 구문을 생각해내는 것이 가능합니다.

type Foo: Bar = _; 이 구문이라고 말하려고 합니까, 아니면 아직 찾지 못했다고 생각합니까?

나는 우리가 창의적이지 못해서가 아니라 대부분의 프로그래머가 유형 이론가가 아니기 때문에 유사한 혼란이 없는 구문을 생각해내는 것이 가능하다고 생각하지 않습니다. 혼동을 허용 가능한 수준으로 줄이는 구문을 찾을 수 있으며 유형 이론 베테랑에게 모호하지 않은 구문이 많이 있지만 혼란을 완전히 제거하지는 못할 것입니다.

당신은 완전히 새로운 표기법을 발명했습니다

한 키워드를 다른 키워드로 교체했다고 생각했습니다. 내가 의도하지 않은 추가 변경 사항이 있습니까?

생각해 보면, 우리는 지금까지 "existential"을 오용해 왔기 때문에 existential Foo: Trait / = impl Trait 아마도 더 이상 합법적인 구문이 아닐 것입니다.

그래서 우리는 알려지지 않은 외부 코드 유형을 참조하는 이름 앞에 놓을 새 키워드가 필요합니다... 그리고 여기에 공백을 그립니다. alias , secret , internal 등은 모두 매우 끔찍하고 type 보다 "고유성 혼란"이 덜할 것 같지 않습니다.

생각해 보면, 우리는 지금까지 "existential"을 오용해 왔기 때문에 existential Foo: Trait / = impl Trait 아마도 더 이상 합법적인 구문이 아닐 것입니다.

예, 전적으로 동의합니다. "실존적"이라는 용어에서 완전히 벗어나야 한다고 생각합니다.* ( impl Trait 잘 설명하면서 이것을 수행하는 방법에 대한 잠정적인 아이디어가 있었습니다)

*( dyn Trait 만 예약 가능)

@joshtriplett , @Ixrec : _ 표기법은 더 이상 이전과 같은 정도로 대체할 수 없다는 것을 의미하며, 이것이 우선 순위를 유지해야 하는 경우 다른 구문이 필요하다는 데 동의합니다.

_ 는 대체와 관련하여 이미 특별한 경우라는 점을 명심하세요. 이것은 단지 유형 별칭에만 영향을 미치는 것이 아닙니다. 현재 _ 사용할 수 있는 모든 곳에서 완전한 참조 투명성을 방지하고 있습니다.

어쨌든 _는 대체와 관련하여 이미 특별한 경우입니다. 이것이 영향을 미치는 것은 단지 유형 별칭이 아닙니다. 현재 _를 사용할 수 있는 모든 곳에서 완전한 참조 투명성을 방지하고 있습니다.

이것이 정확히 무엇을 의미하는지 설명해 주시겠습니까? _ 의 영향을 받는 "참조 투명성"이라는 개념을 알지 못했습니다.

나는 _ 표기법이 더 이상 이전과 같은 정도로 대체할 수 없다는 것을 의미한다는 데 동의합니다. 이것이 우선 순위를 유지해야 한다면 다른 구문이 필요할 것입니다.

_우선순위_인지 잘 모르겠습니다. 나에게 그것은 하나의 구문을 다른 구문보다 선호하는 것처럼 보이는 우리가 발견한 유일한 객관적인 주장이었습니다. 그러나 이는 type 를 대체할 키워드가 무엇인지에 따라 달라질 수 있습니다.

이것이 정확히 무엇을 의미하는지 설명해 주시겠습니까? _ 의 영향을 받는 "참조 투명성"이라는 개념을 알지 못했습니다.

네, 죄송합니다. 설명도 하지 않고 말만 던지고 있습니다. 내 생각을 모아서 좀 더 일관성 있는 설명을 하겠다. impl Trait 를 보는 대안(잠재적으로 더 유용한) 방법과 잘 맞습니다.

참조 투명성 이란 의미 체계의 변경 없이 정의를 참조로 대체하거나 그 반대의 경우도 마찬가지임을 의미합니다. Rust에서 이것은 fn 의 용어 수준에서 분명히 적용되지 않습니다. 예를 들어:

fn foo() -> usize {
    println!("ey!");
    42
}

fn main() {
    let bar = foo();
    let baz = bar + bar;
}

foo() ( bar 의 정의)를 bar 의 각 발생으로 대체하면 분명히 다른 출력을 얻습니다.

그러나 유형 별칭의 경우 현재 AFAIK(참조 투명도 유지)가 유지됩니다. 별칭이 있는 경우:

type Foo = Definition;

그런 다음 프로그램의 의미를 변경하지 않고 Definition Foo 대한 Definition 발생 대체 및 Foo 발생 Definition 대체를 수행할 수 있습니다. , 또는 유형 정확성.

소개:

type Foo = impl Bar;

Foo 의 각 발생이 동일한 유형 임을 의미하는 것은 다음을 작성하는 경우를 의미합니다.

fn stuff() -> Foo { .. }
fn other_stuff() -> Foo { .. }

Fooimpl Bar Foo 로 대체할 수 없으며 그 반대의 경우도 마찬가지입니다. 즉, 다음과 같이 작성하면:

fn stuff() -> impl Bar { .. }
fn other_stuff() -> impl Bar { .. }

반환 유형은 Foo 와 통합되지 않습니다. 따라서 내부에 RFC 2071의 의미 체계와 함께 impl Trait 를 도입하여 유형 별칭에 대한 참조 투명도가 깨졌습니다.

참조 투명성 및 type Foo = _; , 계속... (@varkor 작성)

많은 사람들이 type을 "매번 잠재적으로 다른 유형"으로 해석할 수 있는 간단한 대체 가능한 별칭으로 해석하기 때문에 type 이외의 구문도 도움이 될 것이라고 생각합니다.

좋은 지적. 그러나 = _ 할당 비트가 단일 유형임을 의미하지 않습니까?

전에도 쓴 적이 있지만...

참조 투명성: C 전처리기와 같은 대체 대신 type 를 바인딩(예 let )으로 보는 것이 더 유용하다고 생각합니다. 그런 식으로 보면 type Foo = impl Trait 정확히 무엇을 의미하는지 알 수 있습니다.

나는 초보자가 impl Trait 를 실존적 유형과 보편적 유형으로 생각할 가능성이 적을 것이라고 생각합니다. 그러나 " impl sa Trait . If they want to know more, they can read the impl Trait` 문서. 구문을 변경하면 기존 기능과 큰 이점이 없는 연결이 끊어집니다.

type Foo = _ 다시 _ 에 완전히 관련 없는 의미를 오버로드합니다. 문서 및/또는 Google에서 찾기가 까다로워 보일 수도 있습니다.

@lnicola 전자가 참조적으로 투명한 let 바인딩 대신 const 바인딩을 사용할 수도 있습니다. let ( fn 내에서 참조적으로 투명하지 않음 )를 선택하는 것은 특히 직관적이지 않다고 생각하는 임의의 선택입니다. 나는 그들이 별칭이기 때문에 유형 별칭의 직관적 인 뷰가 (그 단어가 사용되지 않은 경우에도) referentially 투명 있다고 생각합니다.

나는 또한 캡처를 피하고 제네릭(SFINAE 없음)을 존중해야 하기 때문에 C 전처리기 대체로 type 를 보고 있지 않습니다. 대신, 모든 바인딩이 순수한 Idris 또는 Agda와 같은 언어에서 바인딩을 하는 것처럼 정확히 type 를 생각하고 있습니다.

나는 초보자가 impl Trait 를 실존적 유형과 보편적 유형으로 생각할 가능성이 적지만 "특성을 함축하는 것"이라고 생각합니다.

그것은 나에게 차이가 없는 구별처럼 보인다. "existential"이라는 전문 용어는 사용되지 않지만 사용자가 직관적으로 실존적 유형의 개념과 동일한 개념에 연결하고 있다고 생각합니다(Rust의 맥락에서 "어떤 유형의 Foo that impls Bar"에 지나지 않음).

type Foo = _ 다시 _ 에 완전히 관련 없는 의미를 오버로드합니다.

어때요? type Foo = _; 는 유형이 예상되는 다른 컨텍스트에서 _ 를 사용하는 것과 일치합니다.
.collect::<Vec<_>>() 를 작성할 때와 마찬가지로 "실제 유형을 추론"을 의미합니다.

문서 및/또는 Google에서 찾기가 까다로워 보일 수도 있습니다.

그렇게 어렵지 않아야합니까? "유형 별칭 밑줄"이 원하는 결과를 가져와야 합니까...?
"유형 별칭 impl 특성"을 검색하는 것과 다르지 않은 것 같습니다.

Google은 특수 문자를 색인화하지 않습니다. 내 StackOverflow 질문에 밑줄이 있는 경우 Google은 밑줄이라는 단어가 포함된 쿼리에 대해 밑줄을 자동으로 인덱싱하지 않습니다.

@센트릴

어때요? type Foo = _; 는 유형이 예상되는 다른 컨텍스트에서 _를 사용하는 것과 일치합니다.
.collect:를 작성할 때와 마찬가지로 "실제 유형 추론"을 의미합니다.>().

그러나 이 기능은 유형을 유추하지 않고 이에 대한 유형 별칭을 제공합니다. 모듈 또는 크레이트와 같은 제한된 범위를 벗어나 "실제 유형"과 통합되지 않는 실존 유형을 생성합니다.

Google은 특수 문자를 색인화하지 않습니다.

이것은 더 이상 사실이 아닙니다 (비록 공백에 종속적일 수 있지만..?).

그러나 이 기능은 유형을 유추하지 않고 이에 대한 유형 별칭을 제공합니다. 모듈 또는 크레이트와 같은 제한된 범위를 벗어나 "실제 유형"과 통합되지 않는 실존 유형을 생성합니다.

type Foo = _; 의 제안된 의미는 전적으로 추론을 기반으로 하는 존재 유형 별칭을 갖는 대안입니다. 그것이 완전히 명확하지 않은 경우, 의도를 조금 더 잘 설명해야 하는 것으로 곧 후속 조치를 취하겠습니다.

@iopq 최근 변경 사항에 대한 @varkor 의 메모 외에도 다른 검색 엔진의 경우 공식 문서 및 기타 항목에서 type 과 함께 "밑줄"이라는 리터럴 단어를 명시적으로 사용할 수 있다는 점을 추가하고 싶습니다.

어떤 이유로 든 쿼리에 _를 사용하면 여전히 좋은 결과를 얻지 못할 것입니다. 밑줄을 검색하면 밑줄이라는 단어가 포함된 항목을 얻을 수 있습니다. 검색하면 _ 밑줄이 있는 모든 항목이 표시되므로 관련성이 있는지조차 모르겠습니다.

@센트릴

let 선택(fn 내에서 참조적으로 투명하지 않음)은 특히 직관적이지 않다고 생각하는 임의의 선택입니다. 유형 별칭에 대한 직관적인 견해는 별칭이기 때문에 참조적으로 투명하다는 것입니다(해당 단어가 사용되지 않더라도).

죄송합니다. 제 직관이 완전히 거꾸로 되었기 때문에 여전히 이 문제에 대해 머리를 감쌀 수 없습니다.

예를 들어, type Foo = Bar 경우 내 직관은 다음과 같이 말합니다.
" Bar 와 같은 유형이 되는 Foo 선언합니다."

그런 다음 type Foo = impl Bar 라고 쓰면 내 직감은 다음과 같이 말합니다.
" Bar 를 구현하는 유형이 되는 Foo 선언합니다."

Fooimpl Bar 대한 텍스트 별칭이면 저에게는 매우 직관적이지 않습니다. 나는 이것을 텍스트의미 별칭으로 생각하는 것을 좋아합니다.

따라서 Foo 가 나타나는 모든 위치에서 impl Bar 로 대체될 수 있다면 그것은 매크로와 메타프로그래밍을 가장 연상시키는 텍스트 별칭입니다. 그러나 Foo 에 선언 시점에 의미가 할당되고 그 원래 의미(문맥적 의미가 아님!)와 함께 여러 곳에서 사용될 수 있는 경우, 이는 의미론적 별칭입니다.

또한 컨텍스트 실존 유형의 동기를 어쨌든 이해하지 못합니다. 특성 별칭이 정확히 동일한 결과를 얻을 수 있다는 점을 고려할 때 유용할 수 있습니까?

아마도 저는 Haskell이 아닌 배경지식 때문에 참조 투명도가 직관적이지 않다는 것을 알게 될 것입니다. 누가 알겠습니까... :) 하지만 어쨌든, 이것은 확실히 제가 Rust에서 기대하는 그런 종류의 동작이 아닙니다.

@Nemo157 @stjepang

경우 Foo 정말 실존 유형에 대한 유형의 별칭이었다

(강조 내). fg 는 동일한 존재 유형을 참조하기 때문에 다른 구체적인 반환 유형을 지원하지 않는다는 것을 의미하는 '특정'으로 'an'을 읽었습니다.

이것은 "실존 유형"이라는 용어의 오용이거나 적어도 @varkor 의 게시물과 type Foo = impl Bar Foo ∃ T. T: Trait 유형에 대한 별칭으로 만드는 것처럼 보일 수 있습니다. - 그리고 ∃ T. T: Trait 모든 곳에서 대체하면 Foo 아닌 경우

∃ T 수량자의 범위(예에서 existential type _0 )가 문제입니다. APIT에서는 이와 같이 엄격합니다. 호출자는 ∃ T. T: Trait 를 충족하는 모든 값을 전달할 수 있습니다. 하지만하지 2071의 RFC와, RPIT에없는 existential type 선언, 그리고 당신의 desugaring의 예 - 존재의 정량은 전체 기능 또는 전체 모듈 수준에서, 더 멀리 밖으로, 당신은 처리 어디서나 동일한 T .

따라서 모호성 - 우리는 이미 impl Trait 의 수량자를 위치에 따라 다른 위치에 배치하고 있으므로 type T = impl Trait 대해 어느 것을 예상해야 합니까? 일부 비공식 여론 조사와 RFC 2071 스레드 참가자의 사후 실현은 어떤 식으로든 명확하지 않음을 증명합니다.

이것은 우리의 해석에서 멀리 이동하려는 이유는 impl Trait existentials과 관련, 대신 형식 유추의 측면에서 그 의미를 설명하기 위해 전혀 아무것도있다. type T = _ 에는 동일한 종류의 모호성이 없습니다. 여전히 표면 수준 " T 대신 _ 를 복사하여 붙여넣을 수 없습니다."가 있지만 더 이상 없습니다. " T 가 별칭인 단일 유형 은 여러 구체적인 유형을 의미할 수 있습니다." (불투명한/통합되지 않는 동작은 @varkor 가 후속 조치에 대해 이야기하는 것입니다.)

참조 투명성

유형 별칭이 현재 참조 투명도와 호환된다고 해서 사람들이 해당 기능이 이를 따르기를 기대 한다는 의미는 아닙니다.

예를 들어, const 항목을 참조 투명 (https://github.com/rust-lang/rust/issues/34511#issuecomment-402520768에 언급 된)이며, 신규 이전에 실제로 발생하는 혼란 사용자(rust-lang-nursery/rust-clippy#1560).

그래서 저는 Rust 프로그래머에게 참조 투명성이 가장 먼저 생각하는 것은 아니라고 생각합니다.

@stjepang @kennytm 모든 사람이 type Foo = impl Trait; 가 있는 유형 별칭이 참조적으로 투명한 방식으로 작동할 것으로 기대한다고 말하는 것이 아닙니다. 그러나 이 스레드와 다른 곳( @rpjohnst 가 말하는 것...)의 혼란에서 알 수 있듯이 사소하지 않은 사용자가

이 문제에서 무엇을 해야 하는지에 대한 나의 현재 생각은 @varkor 및 @rpjohnst와 일치합니다.

re: 참조 투명성

type Foo<T> = (T, T);

type Bar = Foo<impl Copy>;   // not equivalent to (impl Copy, impl Copy)

즉, 모든 인스턴스에서 새 유형을 생성하는 것조차 제네릭 유형 별칭의 컨텍스트에서 참조적으로 투명하지 않습니다.

@centril Foo in type Foo = impl Bar; 대한 참조 투명성을 기대하면 손을 듭니다 . 그러나 type Foo: Bar = _; 사용하면 참조 투명성을 기대하지 않습니다.

caller 를 단일형화하여 enum impl Trait 와 같은 메커니즘 없이 여러 유형을 지원하도록 반환 위치 impl Trait 를 확장할 수도 있습니다. 이것은이 "강화 impl Trait 가까이에 라인을 제공, 통역 항상 존재이다" dyn Trait , 및 제안 abstract type 사용하지 않는 구문 impl Trait 조금도.

나는 이것을 내부에 썼습니다. https://internals.rust-lang.org/t/extending-impl-trait-to-allow-multiple-return-types/7921

우리가 새로운 실존 유형을 안정화할 때를 위한 참고 사항입니다. "실존"은 항상 (RFC에 따르면) 임시 키워드로 의도되었으며 (IMO) 끔찍합니다. 안정화되기 전에 더 나은 것을 생각해 내야 합니다.

"실존적" 유형에 대한 이야기는 문제를 해결하지 못하는 것 같습니다. impl Trait 는 Trait을 구현하는 특정 유추 유형을 나타냅니다. 그런 식으로 설명하면 type Foo = impl Bar 는 분명히 특정하고 항상 동일한 유형이며 실제로 유용한 유일한 해석이기도 합니다. 따라서 추론된 컨텍스트 이외의 다른 컨텍스트에서 사용할 수 있습니다. 구조체에서처럼.

이런 의미에서 impl Trait_ : Trait 로 쓰는 것도 의미가 있습니다.

@rpjohnst ,

여러 유형을 지원하도록 반환 위치 impl Trait 를 확장할 수도 있습니다.

그것은 IMO를 유용하게 만들 것입니다. impl 유형에 대한 별칭의 요점은 함수가 impl Foo 를 반환하는 것으로 정의할 수 있지만 특정 유형은 여전히 ​​다른 구조체 및 기타 프로그램을 통해 전파된다는 것입니다. 컴파일러가 암시적으로 적절한 enum 생성한 경우 작동하지만 단형화에서는 작동하지 않습니다.

@jan-hudec 이러한 아이디어는 Discord에 대한 토론에서 나왔고, 주로 현재의 return-position 및 argument-position impl Trait 해석이 일치하지 않는다는 사실을 기반으로 하는 몇 가지 문제가 있습니다.

impl Trait 를 특정 유추 유형을 나타내도록 하는 것은 좋은 선택이지만, 이 불일치를 수정하려면 Rust가 현재 가지고 있는 것과는 다른 유형 의 유추여야 합니다. 인수 위치 impl Trait 의 현재 동작. 이것은 아마도 가장 간단한 방법일 것입니다. 그러나 그것은 당신이 말하는 것처럼 간단하지 않습니다.

예를 들어 impl Trait " Trait 를 구현하는 가능한 다형성 유형을 찾기 위해 이 새로운 유형의 추론을 사용하십시오"를 의미하면 type Foo = impl Bar 는 모듈에 대한 것을 암시하기 시작합니다. abstract type 를 추론하는 방법에 관한 RFC 2071 규칙은 모든 용도가 동일한 유형을 독립적으로 추론해야 한다고 말하지만 이 다형성 추론은 적어도 더 많은 것이 가능 하다는 것을 의미 합니다. 우리가 이제까지 (심지어 단지 훨씬 더 그럴듯한 아이디어, 수명 이상) 매개 변수화 모듈을 가지고 있다면, 그 상호 작용의 주위에 질문이있을 것입니다.

또한 어떤 사람들은 "existential"이라는 단어를 이해하는지 여부와 우리가 가르치는 방법에 관계없이 type Foo = impl Bar 구문을 항상 실존자의 별칭으로 해석한다는 사실도 있습니다. 따라서 추론 기반 해석으로 문제가 해결되더라도 대체 구문을 선택하는 것은 여전히 ​​좋은 생각일 것입니다.

또한 _: Trait 구문은 실제로 처음에 추론 기반 해석에 대한 논의에 영감을 주었지만 우리가 원하는 것을 수행하지 않습니다. 첫째, _ 가 암시하는 추론은 다형성이 아니므로 나머지 언어에 대한 잘못된 비유입니다. 둘째, _ 는 실제 유형이 다른 곳에서 표시됨을 의미하는 반면 impl Trait 는 실제 유형을 숨기도록 특별히 설계되었습니다.

마지막으로 내가 monomorphization 제안을 쓴 이유는 인수의 의미와 return-position impl Trait 의 의미를 통일하는 또 다른 방법을 찾는 각도에서였다. 그리고 예, -> impl Trait 가 더 이상 단일 구체적인 유형을 보장하지 않는다는 것을 의미하지만 현재로서는 이를 활용할 방법이 없습니다. 그리고 제안된 솔루션은 모두 성가신 해결 방법입니다. 추가 상용구 abstract type 트릭, typeof 등입니다. 단일 유형 동작에 의존하려는 모든 사람은 abstract type 통해 단일 유형의 이름abstract type 구문(무엇이든 간에)은 틀림없이 전반적으로 이점이 있습니다.

이러한 아이디어는 Discord에 대한 토론에서 나왔고, 주로 현재의 return-position 및 argument-position impl Trait 해석이 일치하지 않는다는 사실을 기반으로 하는 몇 가지 문제가 있습니다.

개인적으로, 나는 이러한 불일치가 실제로 문제가 된다고 생각하지 않습니다. 인수 위치 대 반환 위치 대 유형 위치에 대해 구체적인 유형이 결정되는 범위는 상당히 직관적으로 작동하는 것 같습니다.

호출자가 반환 유형을 결정하는 함수가 있습니다. 물론 거기에는 impl Trait을 사용할 수 없습니다. 차이점을 이해할 때까지 암시하는 것처럼 직관적이지 않습니다.

개인적으로, 나는 이러한 불일치가 실제로 문제가 된다고 생각하지 않습니다.

물론. 이것이 나에게 시사하는 바는 불일치를 무시해야 한다는 것이 아니라 일관성이 있도록 디자인을 다시 설명해야 한다는 것입니다(예: 다형성 유형 추론으로 설명). 이 방법은, 미래의 확장 (RFC 2071 등) 혼동되는 것을 것을 방지하기 위해 새로운 일관된 해석에 대해 확인할 수 있습니다.

@rpjohnst

단일 유형 동작에 의존하려는 모든 사람이 추상 유형 구문(무엇이든 간에)을 통해 단일 유형의 이름도 지정하도록 하는 것은 틀림없이 전반적으로 이점이 있습니다.

어떤 경우에는 그 감정에 동의하지만 클로저 또는 생성기에서는 작동하지 않으며 유형이 무엇인지 신경 쓰지 않고 특정 특성을 구현하는 데 관심이 있는 많은 경우에 인체 공학적이지 않습니다. , 예를 들어 반복자 결합자를 사용합니다.

@mikeyhew RFC 2071 abstract type 구문을 통해 이름을 발명 하는 것에 대해 이야기하고 있기 때문에 @mikeyhew 클로저 또는 기타 이름을 지정할 수 없는 유형에 대해 잘 작동합니다. 다른 곳에서 단일 유형을 사용하려는 경우에 상관없이 이름을 발명 해야 합니다.

@rpjohnst

let x: impl Trait 애타게 기다리고 있습니다.

let x: impl Trait 대한 또 다른 투표로 futures 예제 중 일부를 단순화합니다. 여기에 예제 예제가 있습니다 . 현재 impl Trait 사용하는 기능을 얻기 위해 함수를 사용하고 있습니다.

fn make_sink_async() -> impl Future<Output = Result<
    impl Sink<SinkItem = T, SinkError = E>,
    E,
>> { // ... }

대신 이것은 일반적인 let 바인딩으로 작성할 수 있습니다.

let future_sink: impl Future<Output = Result<
    impl Sink<SinkItem = T, SinkError = E>,
    E,
>> = // ...;

원하는 경우 let x: impl Trait 를 구현하여 누군가를 멘토링할 수 있습니다. 하는 것이 불가능할 정도로 어렵지는 않지만 확실히 쉽지도 않습니다. 진입점:

https://github.com/rust-lang/rust/blob/master/src/librustc/hir/lowering.rs#L3159 에서 반환 유형 impl Trait을 방문하는 방법과 유사하게 https 의 지역 주민 유형을 방문해야 합니다.

그런 다음 현지인 유형을 방문할 때 실제로 활성화하려면 ExistentialContextReturn 로 설정해야 합니다.

이것은 이미 우리를 아주 멀리 데려가야 합니다. 모든 방법이 있는지 확실하지 않지만 return position impl 특성과 100% 같지는 않지만 대부분은 그렇게 행동해야 합니다.

@rpjohnst ,

이러한 아이디어는 Discord에 대한 토론에서 나왔고 주로 현재의 return-position 및 argument-position impl Trait 해석이 일치하지 않는다는 사실을 기반으로 하는 몇 가지 문제가 있습니다.

기사에서 이야기한 범위로 돌아가게 합니다. 그리고 그것들은 실제로 둘러싸는 "괄호"에 해당한다고 생각합니다. 인수 위치의 경우 인수 목록이고 반환 위치의 경우 함수이며 별칭의 경우 별칭이 정의된 범위입니다.

이 스레드의 토론, 원본 RFC 및 동기 토론을 기반으로 existential type 구체적인 구문에 대한 해결을 제안하는 RFC를 열었습니다. https://github.com/rust-lang/rfcs/pull /2515.

현재 존재 유형 구현은 모든 현재 반환 위치 impl Trait 정의를 나타내는 데 사용할 수 없습니다. impl Trait 는 사용하지 않더라도 모든 일반 유형 인수를 캡처하기 때문에 existential type 하지만 사용하지 않는 유형 매개변수 경고가 표시됩니다. (playground)

fn foo<T>(_: T) -> impl ::std::fmt::Display {
    5
}

existential type Bar<T>: ::std::fmt::Display;
fn bar<T>(_: T) -> Bar<T> {
    5
}

값 자체가 사용되지 않더라도 유형 매개변수가 반환된 impl Trait 수명을 제한하는 내부 수명을 가질 수 있기 때문에 중요할 수 있습니다. <T> 에서 Bar 를 제거합니다. 위에서 foo 호출 bar 는 작동하는지 확인합니다.

현재 존재 유형 구현은 모든 현재 반환 위치 impl Trait 정의를 나타내는 데 사용할 수 없습니다.

당신은 할 수 있습니다, 그것은 매우 불편합니다. PhantomData 필드 + 실제 데이터 필드로 newtype을 반환하고 실제 데이터 필드로 전달하는 특성을 구현할 수 있습니다.

@oli-obk 추가 조언 감사합니다. 귀하의 이전 조언과 @cramertj의 일부를 통해

@fasihrana @Nemo157 위 참조. 아마 몇 주 후에! :-)

existential type 유형 매개변수를 암시적으로 캡처 하지 않는 동작( @Nemo157이 언급함)이 의도적이며 그대로 유지될 것임을 누군가 명확히 할 수 있습니까? 나는 그것을 해결하기 때문에 그것을 좋아한다 # 42940

일부러 이렇게 많이 구현했습니다

@Arnavion 예, 이것은 의도적이며 다른 항목 선언(예: 중첩 함수)이 Rust에서 작동하는 방식과 일치합니다.

existential_typenever_type 사이의 상호작용이 이미 논의되었습니까?

아마도 ! 는 관련된 특성에 관계없이 모든 실존 ​​유형을 채울 수 있어야 합니다.

existential type Mystery : TraitThatIsHardToEvenStartImplementing;

fn hack_to_make_it_compile() -> Mystery { unimplemented!() }

아니면 어떤 존재 유형도 자동으로 충족시킬 수 있는 유형 수준 unimplemented!() 되는 특별한 불가촉천적 유형이 있습니까?

@vi 제 생각에는 이는 "절대 유형이 기본이 아닌 자체 메소드 또는 관련 유형 없이 모든 특성을 구현해서는 안 됩니다"라는 일반적인 사항에 해당한다고 생각합니다. 그러나 그것이 어디에서 추적될지는 모른다.

곧 특성 메서드 반환 유형에 대한 지원을 확장할 계획이 있습니까?

existential type 이미 특성 메서드에서 작동합니다. Wrt impl Trait , RFC에서도 적용됩니까?

@alexreg fn foo<T>(..) -> impl Bar<T> (대략 -> Self::AnonBar0<T> )와 같은 항목이 있을 때 GAT가 익명 관련 유형으로 디슈거할 수 있어야 한다고 생각합니다.

@Centril 거기 impl Bar<T> 를 하려는 건가요? 의 암시 적 타입 캡처 동작 impl Trait 당신도 같은과 GATS에 대해 같은 필요성을 얻을 수단 fn foo<T>(self, t: T) -> impl Bar; .

@Nemo157 아니요 죄송합니다. 그러나 귀하의 예는 문제를 훨씬 더 잘 보여줍니다. 고맙습니다 :)

@alexreg fn foo와 같은 것이 있을 때 GAT가 익명 관련 유형으로 디슈거할 수 있어야 한다고 생각합니다.(..) -> impl 바(대략 -> Self::AnonBar0 됩니다).

아, 알겠습니다. 솔직히 꼭 필요한 것 같지는 않지만 확실히 구현하는 방법 중 하나입니다. GAT에서 움직임이 없다는 것은 약간 걱정스럽긴 하지만... 오랫동안 아무 소식도 듣지 못했습니다.

Triage: https://github.com/rust-lang/rust/pull/53542 가 병합되었으므로 {let,const,static} foo: impl Trait 체크 박스를 확인할 수 있다고 생각합니다.

다음과 같이 쓸 수 있습니까?

trait Foo {
    fn GetABar() -> impl Bar;
}

??

아마 아닐 것입니다. 그러나 우리가 얻을 수 있도록 모든 것을 준비하는 지속적인 계획이 있습니다.

trait Foo {
    type Assoc: Bar;
    fn get_a_bar() -> Assoc;
}

impl Foo for SomeType {
    fn get_a_bar() -> impl Bar {
        SomeThingImplingBar
    }
}

다음과 같은 형태로 야간에 이 기능을 실험할 수 있습니다.

impl Foo for SomeType {
    existential type Assoc;
    fn get_a_bar() -> Assoc {
        SomeThingImplingBar
    }
}

이것에 대한 더 많은 정보를 얻기 위한 좋은 시작은 https://github.com/rust-lang/rfcs/pull/2071 (및 여기에 연결된 모든 것)입니다.

@oli-obk on rustc 1.32.0-nightly (00e03ee57 2018-11-22) , impl 블록에서 작동하려면 existential type 에 대한 특성 경계도 지정해야 합니다. 예상인가요?

@jonhoo 가 특성을 지정할 수 있다는 것은 필요한 특성 이상을 제공할 수 있기 때문에 유용합니다.

impl Foo for SomeDebuggableType {
    existential type Assoc: Bar + Debug;
    fn get_a_bar() -> Assoc {
        SomeThingImplingBarAndDebug
    }
}

fn use_debuggable_foo<F>(f: F) where F: Foo, F::Assoc: Debug {
    println!("bar is: {:?}", f.get_a_bar())
}

필수 특성은 존재하는 연관 유형에 암시적으로 추가될 수 있으므로 확장할 때 경계만 필요하지만 개인적으로 구현에 넣어야 하는 로컬 문서를 선호합니다.

@Nemo157 아, 죄송합니다. 제가

impl A for B {
    existential type Assoc;
    // ...
}

반면 이것은 다음을 수행합니다.

impl A for B {
    existential type Assoc: Debug;
    // ...
}

아, 특성이 연관된 유형의 경계를 요구하지 않는 경우에도 여전히 실존 유형(비어 있을 수 있음)( 놀이터 )에 경계를 제공해야 합니다.

trait Foo {
    type Assoc;
    fn foo() -> Self::Assoc;
}

struct Bar;
impl Foo for Bar {
    existential type Assoc: ;
    fn foo() -> Self::Assoc { Bar }
}

이것은 경계가 없는 존재 유형을 갖는 것이 사용자에게 _no_ 작업을 제공한다는 것을 의미합니다(자동 특성 제외). 그래서 무엇을 위해 사용될 수 있습니까?

또한 -> impl Trait 와 동일한 작업을 수행할 수 있는 방법이 없으며 -> impl () 는 구문 오류이며 -> impl 자체에서 error: at least one trait must be specified . 존재 유형 구문이 type Assoc = impl Debug; 또는 이와 유사하게 되면 적어도 하나의 특성 경계 없이 연관된 유형을 지정할 방법이 없는 것처럼 보입니다.

@Nemo157 네, 위에서 제안한 코드를 말 그대로 시도했기 때문에 깨달았습니다. 그리고 작동하지 않았습니다. 예를 들어:

trait Foo {
    type Assoc: Future<Output = u32>;
}

struct Bar;
impl Foo for Bar {
    existential type Assoc;
}

Future<Output = u32> 를 두 번 지정하지 않아도 되는 것이 합리적으로 보이지만 작동하지 않습니다. existential type Assoc: ; (매우 이상한 구문처럼 보임)도 해당 추론을 수행하지 않을 것이라고 가정합니다.

trait Foo {
    type Assoc;
    fn foo() -> Self::Assoc;
}

struct Bar;
impl Foo for Bar {
    existential type Assoc: ;
    fn foo() -> Self::Assoc { Bar }
}

이것은 경계가 없는 존재 유형을 갖는 것이 사용자에게 _no_ 작업을 제공한다는 것을 의미합니다(자동 특성 제외). 그래서 무엇을 위해 사용될 수 있습니까?

동일한 특성 구현에서 소비에 사용할 수 없습니까? 이 같은:

trait Foo {
    type Assoc;
    fn create_constructor() -> Self::Assoc;
    fn consume(marker: Self::Assoc) -> Self;
    fn consume_box(marker: Self::Assoc) -> Box<Foo>;
}

약간 인위적이지만 유용할 수 있습니다. 평생 동안 실제 구조체보다 먼저 일부 예비 부품을 구성해야 하는 상황을 상상할 수 있습니다. 또는 다음과 같을 수 있습니다.

trait MarkupSystem {
    type Cache;
    fn create_cache() -> Cache;
    fn translate(cache: &mut Self::Cache, input: &str) -> String;
}

이 두 경우 모두 existential type Assoc; 가 유용합니다.

impl Trait에 대한 관련 유형을 정의하는 적절한 방법은 무엇입니까?

예를 들어, Action 특성이 있고 특성의 관련 유형 구현이 전송 가능한지 확인하려면 다음과 같이 하면 됩니다.

pub trait Action {
    type Result;
    fn call(&self) -> Self::Result;
}

impl MyStruct {
    pub fn new(name: String) -> impl Action 
    where 
        Return::Result: Send //This Return should be the `impl Action`
    {
        ActionImplementation::new()
    }
}

이것이 현재 불가능합니까?

@acycliczebra 내 생각에 이에 대한 구문은 -> impl Action<Result = impl Send> 입니다. 예를 들어 다른 익명 impl Trait 유형을 사용하는 -> impl Iterator<Item = u32> 와 같은 구문입니다.

impl Trait 구문을 구조체 필드와 같은 것으로 확장하는 것에 대한 논의가 있었습니까? 예를 들어, 내 공개 인터페이스의 특정 반복자 유형에 대한 래퍼를 구현하는 경우:

struct Iter<'a> {
    inner: std::collections::hash_map::Iter<'a, i32, i32>,
}

특정 특성 경계를 만족하는 한 실제 유형에 대해 별로 신경 쓰지 않는 상황에서 유용할 것입니다. 이 예제는 간단하지만 과거에 여러 개의 중첩 유형 매개변수를 사용하여 매우 긴 유형을 작성하는 상황이 발생했으며 이것이 ExactSizeIterator .

그러나 IIRC에서는 현재 impl Trait 로 다중 경계를 지정하는 방법이 없다고 생각하므로 Clone 와 같은 몇 가지 유용한 것을 잃을 것입니다.

@AGausmann 주제에 대한 최신 토론은 https://github.com/rust-lang/rfcs/pull/2515에 있습니다. 이렇게 하면 type Foo = impl Bar; struct Baz { field: Foo } ... 라고 말할 수 있습니다. type Foo = impl Bar; 안정화한 후 field: impl Trait 를 설탕으로 고려하는 것이 좋습니다. 합리적인 매크로 친화적 인 편의 확장처럼 느껴집니다.

@센트릴 ,

제 생각에는 field: impl Trait 를 설탕으로 간주하는 것이 좋습니다.

나는 이것이 합리적이지 않다고 생각합니다. 구조체 필드에는 여전히 구체적인 유형이 있어야 하므로 컴파일러에 연결된 함수의 반환을 알려야 합니다. 유추할 수는 있지만 함수가 여러 개라면 어떤 함수인지 찾기가 쉽지 않을 것입니다. Rust의 일반적인 정책은 이러한 경우에 명시적으로 지정하는 것입니다.

유추할 수는 있지만 기능이 여러 개라면 어떤 기능인지 찾기가 쉽지 않을 것입니다.

부모 유형에 대한 용도 정의에 대한 요구 사항을 버블링합니다. 그러면 부모 유형을 반환하는 동일한 모듈의 모든 함수가 됩니다. 찾기가 그렇게 어렵지는 않은 것 같습니다. 그러나 확장을 진행하기 전에 type Foo = impl Bar; 에 대한 이야기를 해결하고 싶습니다.

현재 existential type 구현에서 버그를 찾은 것 같습니다.


암호

trait Collection {
    type Element;
}
impl<T> Collection for Vec<T> {
    type Element = T;
}

existential type Existential<T>: Collection<Element = T>;

fn return_existential<I>(iter: I) -> Existential<I::Item>
where
    I: IntoIterator,
    I::Item: Collection,
{
    let item = iter.into_iter().next().unwrap();
    vec![item]
}


오류

error: type parameter `I` is part of concrete type but not used in parameter list for existential type
  --> src/lib.rs:16:1
   |
16 | / {
17 | |     let item = iter.into_iter().next().unwrap();
18 | |     vec![item]
19 | | }
   | |_^

error: defining existential type use does not fully define existential type
  --> src/lib.rs:12:1
   |
12 | / fn return_existential<I>(iter: I) -> Existential<I::Item>
13 | | where
14 | |     I: IntoIterator,
15 | |     I::Item: Collection,
...  |
18 | |     vec![item]
19 | | }
   | |_^

error: could not find defining uses
  --> src/lib.rs:10:1
   |
10 | existential type Existential<T>: Collection<Element = T>;
   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

운동장

stackoverflow 에서도 찾을 수 있습니다.

이 경우를 즉시 지원할 수 있는지 100% 확신할 수 없지만, 할 수 있는 일은 함수를 다시 작성하여 두 개의 일반 매개변수를 갖는 것입니다.

https://play.rust-lang.org/?version=nightly&mode=debug&edition=2018&gist=b4e53972e35af8fb40ffa9a735c6f6b1

fn return_existential<I, J>(iter: I) -> Existential<J>
where
    I: IntoIterator<Item = J>,
{
    let item = iter.into_iter().next().unwrap();
    vec![item]
}

감사 해요!
예, 이것이 stackoverflow 게시물에 게시된 대로 수행한 것입니다.

fn return_existential<I, T>(iter: I) -> Existential<T>
where
    I: IntoIterator<Item = T>,
    I::Item: Collection,
{
    let item = iter.into_iter().next().unwrap();
    vec![item]
}

특성 컨텍스트 내에서 impl Trait 을 사용할 수 있는 계획이 있습니까?
연결된 유형뿐만 아니라 메서드의 반환 값으로도 사용됩니다.

특성의 impl 특성은 여기에서 추적되는 특성과 별개의 기능이며 현재 RFC가 없습니다. 이 공간에는 상당히 오랜 디자인 역사가 있으며 2071(실존 유형)의 구현이 안정화될 때까지 추가 반복이 보류되고 있습니다. 이는 구현 문제와 해결되지 않은 구문(별도의 RFC가 있음)으로 인해 차단됩니다.

@cramertj 구문이 거의 해결되었습니다. 나는 주요 차단기가 지금 GAT라고 생각합니다.

@alexreg : https://github.com/rust-lang/rfcs/pull/2515 는 여전히 @withoutboats를 기다리고 있습니다.

@varkor 예, 나는 그들이 곧 그 RFC로 빛을 보게 될 것이라고 낙관하고 있습니다. ;-)

다음과 같은 것이 가능할까요?

#![feature(existential_type)]

trait MyTrait {}

existential type Interface: MyTrait;

struct MyStruct {}
impl MyTrait for MyStruct {}

fn with<F, U>(cb: F) -> U
where
    F: FnOnce(&mut Interface) -> U
{
    let mut s = MyStruct {};
    cb(&mut s)
}

hint 함수만 있으면 Interface 의 구체적인 유형을 지정할 수 있지만 지금은 이 작업을 수행할 수 있습니다.

#![feature(existential_type)]

trait MyTrait {}

existential type Interface: MyTrait;

struct MyStruct {}
impl MyTrait for MyStruct {}

fn with<F, U>(cb: F) -> U
where
    F: FnOnce(&mut Interface) -> U
{

    fn hint(x: &mut MyStruct) -> &mut Interface { x }

    let mut s = MyStruct {};
    cb(hint(&mut s))
}

콜백이 인수 유형을 선택할 수 있다면 어떻게 작성하시겠습니까? 실제로 nvm, 일반 제네릭을 통해 해결할 수 있다고 생각합니다.

@CryZe 당신이 찾고 있는 것은 impl Trait 와 관련이 없습니다. 내가 아는 모든 정보는 https://github.com/rust-lang/rfcs/issues/2413 을 참조

잠재적으로 다음과 같이 보일 것입니다.

trait MyTrait {}

struct MyStruct {}
impl MyTrait for MyStruct {}

fn with<F, U>(cb: F) -> U
where
    F: for<I: Interface> FnOnce(&mut I) -> U
{
    let mut s = MyStruct {};
    cb(hint(&mut s))
}

@KrishnaSannasi 아, 흥미

이것이 작동해야합니까?

#![feature(existential_type)]

trait MyTrait {
    type AssocType: Send;
    fn ret(&self) -> Self::AssocType;
}

impl MyTrait for () {
    existential type AssocType: Send;
    fn ret(&self) -> Self::AssocType {
        ()
    }
}

impl<'a> MyTrait for &'a () {
    existential type AssocType: Send;
    fn ret(&self) -> Self::AssocType {
        ()
    }
}

trait MyLifetimeTrait<'a> {
    type AssocType: Send + 'a;
    fn ret(&self) -> Self::AssocType;
}

impl<'a> MyLifetimeTrait<'a> for &'a () {
    existential type AssocType: Send + 'a;
    fn ret(&self) -> Self::AssocType {
        *self
    }
}

existential_type 기능에 대해 existential 키워드를 언어로 유지해야 합니까?

@jethrogb 네. 현재 그렇지 않다는 사실은 버그입니다.

@cramertj 알겠습니다 . 이에 대해 별도의 문제를 제기해야 합니까 아니면 여기에 내 게시물로 충분합니까?

문제를 제출하는 것이 좋습니다. 감사합니다! :)

existential_type 기능에 대해 existential 키워드를 언어로 유지해야 합니까?

type-alias-impl-trait 기능이 구현될 때(즉, lint에 삽입) 이 기능을 즉시 사용하지 않고 결국 구문에서 제거하려는 의도라고 생각합니다.

누군가는 분명히 할 수 있습니다.

impl Trait 보다 일반적으로 추적하는 메타 문제를 위해 이것을 닫습니다: https://github.com/rust-lang/rust/issues/63066

impl Trait를 사용하는 방법에 대한 좋은 예는 어디에도 없습니다. 매우 슬픈

이 페이지가 도움이 되었나요?
0 / 5 - 0 등급