카피 유형에 대한 차용 특성의 반대?
나는 본 적이 Borrow
소유 한 유형 또는 참조, 예를 들어 모두 동의 기능을 정의하는 데 사용되는 특성 T
또는 &T
. borrow()
Methods를 획득하는 함수 호출된다 &T
.
대향 (즉, 허용 함수 있도록 일부 특성 있는가 T
또는 &T
획득하고 T
대) Copy
유형?
예를 들면 다음과 같습니다.
use std::borrow::Borrow;
fn foo<T: Borrow<u32>>(value: T) -> u32 {
*value.borrow()
}
fn main() {
println!("{}", foo(&5));
println!("{}", foo(5));
}
이것은 borrow()
참조를 얻기 위해 호출 하고 즉시 역 참조됩니다.
T
전달 된 경우 값을 복사하고 &T
제공된 경우 역 참조하는 다른 구현이 있습니까? 아니면 위의 것이 이런 종류의 글을 쓰는 관용적 방법입니까?
답변
Borrow
같은 방식으로 함수에 대한 경계로 실제로 유용하지 않기 때문에에 대한 역 특성은 실제로 없습니다 Borrow
. 그 이유는 소유권과 관련이 있습니다.
왜 "역 Borrow
"이 덜 유용 Borrow
합니까?
참조가 필요한 함수
인수를 참조하기 만하면되는 함수를 고려하십시오.
fn puts(arg: &str) {
println!("{}", arg);
}
수락 String
은 puts
데이터의 소유권을 가질 필요가 없기 때문에 여기서는 어리석은 일이지만 수락 &str
은 때때로 호출자가 데이터를 필요 이상으로 오래 유지하도록 강요 할 수 있음 을 의미합니다.
{
let output = create_some_string();
output.push_str(some_other_string);
puts(&output);
// do some other stuff but never use `output` again
} // `output` isn't dropped until here
문제는에 output
전달 된 후 필요하지 않으며 puts
호출자가 이것을 알고 있지만 puts
참조가 필요하므로 output
블록이 끝날 때까지 살아 있어야합니다. 분명히 더 많은 블록을 추가하고 때로는를 추가하여 호출자에서 이것을 수정할 수 let
있지만 puts
호출자 가 정리 책임을 위임 하도록 일반화 할 수도 있습니다 output
.
fn puts<T: Borrow<str>>(arg: T) {
println!("{}", arg.borrow());
}
T: Borrow
for puts
를 수락 하면 호출자가 인수를 유지하거나 함수로 이동할지 여부를 유연하게 결정할 수 있습니다.
소유 값이 필요한 함수
이제 실제로 소유권을 가져야하는 함수의 경우를 고려하십시오.
struct Wrapper(String);
fn wrap(arg: String) -> Wrapper {
Wrapper(arg)
}
이 경우 수용 &str
하기 때문에, 바보가 될 것입니다 wrap
전화를 할 것입니다 to_owned()
그것을. 호출자가 String
더 이상 사용하지 않는가 있으면 방금 함수로 이동할 수있는 데이터를 불필요하게 복사합니다. 이 경우 수락 String
은 호출자가 복제본을 만들지 또는 기존를 전달할지 결정할 수 있기 때문에 더 유연한 옵션 String
입니다. "역 Borrow
"특성 을 갖는 arg: String
것은 아직 제공하지 않는 유연성을 추가 하지 않습니다.
그러나 String
문자열의 여러 가지 종류가 있기 때문에, 항상 가장 인체 공학적인 인수되지 않습니다 : &str
, Cow<str>
, Box<str>
... 우리는 할 수 wrap
는 변환 할 수있는 모든 것을 받아 말하면서 조금 더 인체 공학적 into
을 String
.
fn wrap<T: Into<String>>(arg: T) -> Wrapper {
Wrapper(arg.into())
}
즉 , 리터럴 wrap("hello, world")
을 호출하지 않고도 다음과 같이 호출 할 수 있습니다 .to_owned()
. 이것은 실제로 유연성이있는 승리 는 아닙니다 . 발신자는 .into()
일반성을 잃지 않고 항상 대신 전화를 걸 수 있지만 인체 공학적 승리입니다.
무엇에 대한 Copy
유형?
이제 Copy
유형 에 대해 물었습니다 . 대부분의 경우 위의 주장이 여전히 적용됩니다. 당신은 같은 함수 작성하는 경우 puts
만 필요 &A
하여, T: Borrow<A>
발신자에 대한보다 유연한 수 있습니다을; wrap
전체를 필요 로하는 함수 A
의 경우 A
. 그러나 Copy
유형의 경우 수용 의 인체 공학적 이점 T: Into<A>
은 훨씬 덜 명확합니다.
- 정수 유형의 경우 제네릭이 유형 추론을 엉망으로 만들기 때문에 일반적으로 리터럴을 사용 하는 것이 인체 공학적 이지 않습니다 . 유형에 명시 적으로 주석을 달아야 할 수도 있습니다.
&u32
구현하지 않기 때문에Into<u32>
그 특정 트릭은 어쨌든 여기서 작동하지 않을 것입니다.- 때문에
Copy
유형이 소유 한 값으로 쉽게 사용할 수 있습니다, 그것은 처음에 참조로 사용할 덜 일반적입니다. - 마지막으로, a
&A
를A
when 으로 바꾸는 것은 다음 을 추가하는A: Copy
것만큼이나 간단합니다*
. 이 단계를 건너 뛸 수 있다는 것은 대부분의 경우 제네릭을 사용하는 데 따른 복잡성을 상쇄하기에 충분히 설득력이 없을 것입니다.
결론적으로, foo
거의 확실하게 수락 value: u32
하고 발신자가 그 가치를 얻는 방법을 결정하도록해야합니다.
또한보십시오
- 메서드가 값의 소유권을 필요로 할 때 값으로 전달하거나 참조로 전달하는 것이 더 일반적입니까?
가지고있는 기능을 사용하면 u32
으로 빌릴 수 있는 또는 유형 만 사용할 수 있습니다 u32
.
두 번째 템플릿 인수를 사용하여 함수를보다 일반적으로 만들 수 있습니다.
fn foo<T: Copy, N: Borrow<T>>(value: N) -> T {
*value.borrow()
}
그러나 이것은 일부 경우에 올바르게 작동하기 위해 유형 주석이 필요하므로 부분적인 해결책 일뿐입니다.
예를 들어 다음과 같이 즉시 작동합니다 usize
.
let v = 0usize;
println!("{}", foo(v));
여기에 컴파일러 foo(v)
가 usize
.
그러나을 시도 foo(&v)
하면 컴파일러는 다른 유형에 대해 여러 특성을 구현할 수 T
있기 때문에 올바른 출력 유형을 찾을 수 없다고 불평합니다 . 출력으로 사용할 것을 명시 적으로 지정해야합니다.&T
Borrow
let output: usize = foo(&v);