如何编写以下代码?看起来绝对安全,但是无法说服编译器。
匹配@Builder
class SomeObject {
@NonNull String mandatoryField1;
@NonNull String mandatoryField2;
Integer optionalField;
...
public static SomeObjectBuilder builder() { //class name convention by Lombok
return new CustomBuilder();
}
public static class CustomBuilder extends SomeObjectBuilder {
private static ValidationFactory vf = Validation.buildDefaultValidationFactory();
private Validator validator = vf.getValidator();
@Overrride
public SomeObject build() {
SomeObject result = super.build();
validateObject(result);
return result;
}
private void validateObject(Object object) {
//if object is null throw new IllegalArgException or ValidationException
Set<ConstraintVioletion<Object>> violations = validator.validate(object);
if (violations.size() > 0) {
//iterate through violations and each one has getMessage(), getPropertyPath()
// - to build up detailed exception message listing all violations
[...]
throw new ValidationException(messageWithAllViolations) }
}
}
的版本会在匹配行上显示错误:*self
匹配cannot move out of borrowed content
的版本给出:self
use of moved value: *self
我觉得我需要一个如下所示的匹配臂,但它似乎不是有效的语法:
enum Foo {
Foo1(u32),
Foo2(i16),
}
impl Foo {
fn bar(&mut self, y: u32) -> (u32, &mut Foo) {
match (*self, y) {
(Foo::Foo1(ref mut a), b) if (b == 5) => {
print!("is five");
*a = b + 42;
(b, self)
}
(Foo::Foo2(ref mut a), b) if (b == 5) => {
print!("is five");
*a = (b + 42) as i16;
(*a * b, self)
}
_ => {
print!("is not five!");
(y, self)
}
}
}
}
(ref mut f @ Foo::Foo1, b) if (b == 5) => {
print!("is five");
f.0 = b + 42;
(b, f)
}
答案 0 :(得分:2)
不,这不安全。您正在尝试在匹配臂中引入可变别名。可变引用a
指向与self
相同的值。可以更改self
(例如*self = Foo::Foo1(99)
),这会使a
无效,因此该代码是不允许的。
相反,在self
语句中可变地重新借入 match
,并使它返回元组的第一个值。由于此值没有引用self
,因此您可以返回self
并返回match
的结果:
enum Foo {
Foo1(u32),
Foo2(u32), // changed so I don't have to figure out what casting you meant
}
impl Foo {
fn bar(&mut self, y: u32) -> (u32, &mut Foo) {
let next = match (&mut *self, y) {
(Foo::Foo1(a), b @ 5) => {
*a = b + 42;
b
}
(Foo::Foo2(a), b @ 5) => {
*a = b + 42;
*a * b
}
_ => y,
};
(next, self)
}
}
但是,在这里返回self
却毫无意义。呼叫者已经已经有了&mut Foo
,因此您无需“退还”。这样可以简化为:
impl Foo {
fn bar(&mut self, y: u32) -> u32 {
match (self, y) {
(Foo::Foo1(a), b @ 5) => {
*a = b + 42;
b
}
(Foo::Foo2(a), b @ 5) => {
*a = b + 42;
*a * b
}
_ => y,
}
}
}
我仍然会说这是一个安全的操作,尽管编译器可能无法理解
使用non-lexical lifetimes,借阅检查器变得更加智能。具有原始显式重新借用的原始代码将进行编译:
#![feature(nll)]
enum Foo {
Foo1(u32),
Foo2(u32), // changed so I don't have to figure out what casting you meant
}
impl Foo {
fn bar(&mut self, y: u32) -> (u32, &mut Foo) {
match (&mut *self, y) {
(Foo::Foo1(a), b @ 5) => {
*a = b + 42;
(b, self)
}
(Foo::Foo2(a), b @ 5) => {
*a = b + 42;
(*a * b, self)
}
_ => (y, self),
}
}
}
另请参阅: