2015-01-11 13 views
12

Một FnMut không thể được nhân bản, vì lý do rõ ràng.Bạn có thể nhân bản một đóng cửa không?

Tuy nhiên, Fn có phạm vi bất biến; là có một số cách để tạo ra một 'trùng lặp' của một Fn?

Đang cố gắng để sao chép nó kết quả trong:

&mut core::ops::Fn(logger::Level, &'a collections::string::String) + Send does not implement any method in scope named clone

Hoặc là nó an toàn để bằng cách nào đó vượt qua một con trỏ liệu đến một Fn xung quanh, như:

let func_pnt = &mut Box<Fn<...> + Send> as *mut Box<Fn<...>> 

Về mặt kỹ thuật các công trình trên, nhưng có vẻ như khá lạ.

Humm, ok, đây là một ví dụ về những gì tôi đang cố gắng để làm:

#![feature(unboxed_closures)] 

use std::thread::Thread; 
use std::clone::Clone; 

struct WithCall { 
    fp:Box<Fn<(i8,i8), i8> + Send> 
} 

impl WithCall { 
    pub fn new(fp:Box<Fn<(i8,i8), i8> + Send>) -> WithCall { 
    return WithCall { 
     fp: fp 
    }; 
    } 

    pub fn run(&self, a:i8, b:i8) -> i8 { 
    return self.fp.call((a, b)); 
    } 
} 

unsafe impl Send for WithCall {} 

impl Clone for WithCall { 
    fn clone(&self) -> WithCall { 
    return WithCall { 
     fp: self.fp.clone() 
    }; 
    } 
} 

fn main() { 
    let adder = WithCall::new(Box::new(|&:a:i8, b:i8| -> i8 { 
    return a + b; 
    })); 
    println!("{}", adder.run(1, 2)); 

    let add_a = adder.clone(); 
    let add_b = adder.clone(); 
    Thread::scoped(move || { 
    println!("In remote thread: {}", add_a.run(10, 10)); 
    }); 
    Thread::scoped(move || { 
    println!("In remote thread: {}", add_b.run(10, 10)); 
    }); 
} 

tức. Bạn có một cấu trúc với một đóng cửa đóng hộp trong nó, bạn cần phải vượt qua cấu trúc đó cho một số nhiệm vụ; rõ ràng là bạn không thể. ... nhưng bạn cũng không thể sao chép nó, bởi vì bạn không thể sao chép một Box<Fn<>> và bạn không thể sao chép một playpen &Fn<...>

: http://is.gd/1oNPYJ

+0

gì bạn muốn làm với việc đóng cửa nhân bản? – Shepmaster

+0

Mã đầy đủ của bạn là gì? – huon

+0

@shepmaster Tôi đặc biệt muốn sao chép một đóng cửa mà không có trạng thái có thể thay đổi để di chuyển nó vào nhiều tác vụ cùng một lúc. Xem ví dụ tôi đã đính kèm. – Doug

Trả lời

1

Hãy nhớ rằng việc đóng cửa chụp môi trường của họ, vì vậy họ có một cuộc sống của riêng họ, dựa trên môi trường. Tuy nhiên, bạn có thể mất tài liệu tham khảo cho Fn* và vượt qua những người xung quanh hơn nữa, hoặc lưu trữ chúng trong một cấu trúc:

fn do_more<F>(f: &F) -> u8 
    where F: Fn(u8) -> u8 
{ 
    f(0) 
} 

fn do_things<F>(f: F) -> u8 
    where F: Fn(u8) -> u8 
{ 
    // We can pass the reference to our closure around, 
    // effectively allowing us to use it multiple times. 
    f(do_more(&f)) 
} 

fn main() { 
    let val = 2; 
    // The closure captures `val`, so it cannot live beyond that. 
    println!("{:?}", do_things(|x| (x + 1) * val)); 
} 

tôi sẽ nói rằng nó không phải là phổ biến an toàn để chuyển đổi các Fn* đến một con trỏ liệu và vượt qua nó xung quanh , do mối quan tâm suốt đời.

11

Những gì bạn đang cố gắng làm là gọi một bao đóng từ nhiều luồng. Đó là, chia sẻ việc đóng cửa trên nhiều luồng. Ngay khi cụm từ "chia sẻ qua nhiều chủ đề" vượt qua tâm trí của tôi, suy nghĩ đầu tiên của tôi là to reach for Arc (ít nhất là cho đến khi RFC 458 được triển khai ở một dạng nào đó, khi & sẽ trở thành có thể sử dụng trên các chuỗi). Điều này cho phép bộ nhớ chia sẻ an toàn (nó thực hiện Clone mà không yêu cầu kiểu nội bộ của nó là Clone, vì Clone chỉ tạo một con trỏ mới vào cùng bộ nhớ), và bạn có thể có một đối tượng Fn được sử dụng trong nhiều chủ đề, không cần để nhân đôi nó.

Tóm lại, hãy đặt WithCall trong một Arc và sao chép điều đó.

#![allow(unstable)] 

use std::thread::Thread; 
use std::sync::Arc; 

type Fp = Box<Fn(i8,i8) -> i8 + Send + Sync>; 

struct WithCall { 
    fp: Fp 
} 

impl WithCall { 
    pub fn new(fp: Fp) -> WithCall { 
     WithCall { fp: fp } 
    } 

    pub fn run(&self, a: i8, b: i8) -> i8 { 
     (*self.fp)(a, b) 
    } 
} 

fn main() { 
    let adder = WithCall::new(Box::new(|&: a: i8, b| a + b)); 
    println!("{}", adder.run(1, 2)); 

    let add_a = Arc::new(adder); 
    let add_b = add_a.clone(); 
    Thread::scoped(move || { 
     println!("In remote thread: {}", add_a.run(10, 10)); 
    }); 
    Thread::scoped(move || { 
     println!("In remote thread: {}", add_b.run(10, 10)); 
    }); 
} 

playpen

Lưu ý: Tôi đã gỡ bỏ cổng unboxed_closures tính năng bằng cách sử dụng các đường khuyến cáo Fn(...) -> ... với nhiều loại và trực tiếp () cuộc gọi (không sử dụng .call). Ngoài ra, tôi đã xóa số unsafe impl Send không cần thiết, vì Send được tự động triển khai nếu nội dung. unsafe impl s chỉ được yêu cầu nếu nội dung không phải là Send theo mặc định và lập trình viên muốn ghi đè phán quyết bảo thủ của trình biên dịch.


câu trả lời cũ (điều này vẫn còn có liên quan): Nó là khá bất thường để có một đối tượng &mut Fn đặc điểm, vì Fn::call mất &self. Các mut là không cần thiết, và tôi nghĩ rằng nó cho biết thêm nghĩa là không có thêm chức năng. Có một số &mut Box<Fn()> thêm một số chức năng, nhưng nó cũng không bình thường.

Nếu bạn thay đổi thành con trỏ & thay vì &mut, mọi thứ sẽ hoạt động tự nhiên hơn (với cả hai &Fn&Box<Fn>). Nếu không nhìn thấy mã thực tế mà bạn đang sử dụng, nó rất khó để biết chính xác những gì bạn đang làm, nhưng

fn call_it(f: &Fn()) { 
    (*f)(); 
    (*f)(); 
} 
fn use_closure(f: &Fn()) { 
    call_it(f); 
    call_it(f); 
} 

fn main() { 
    let x = 1i32; 
    use_closure(&|| println!("x is {}", x)); 
} 

(Điều này một phần do &TCopy và cũng một phần là do vay lại; nó hoạt động với &mut . cũng)

Ngoài ra, bạn có thể đóng cửa giao việc đóng cửa, trong đó có khả năng làm việc trong những tình huống hơn:

fn foo(f: &Fn()) { 
    something_else(|| f()) 
} 

Obviously a FnMut cannot be cloned, for obvious reasons.

Không có lý do cố hữu nào mà không thể sao chép được FnMut, nó chỉ là cấu trúc với một số trường (và một phương thức mất &mut self, thay vì &self hoặc self tương ứng với FnFnOnce). Nếu bạn tạo cấu trúc và triển khai FnMut theo cách thủ công, bạn vẫn có thể triển khai Clone cho cấu trúc đó.

Or is it safe to somehow pass a raw pointer to a Fn around, like:

let func_pnt = &mut Box<Fn<...> + Send> as *mut Box<Fn<...>> 

Technically the above works, but it seems quite weird.

Về mặt kỹ thuật nó hoạt động nếu bạn cẩn thận để đảm bảo các yêu cầu răng cưa và tuổi thọ của Rust hài lòng ... nhưng bằng cách chọn trong để con trỏ không an toàn bạn đang đặt gánh nặng trên mình, không để xảy ra trình biên dịch giúp đỡ bạn. Nó là tương đối hiếm hoi mà phản ứng đúng với lỗi biên dịch là sử dụng mã unsafe, thay vì delving vào lỗi và tinh chỉnh mã để làm cho nó có ý nghĩa hơn (để trình biên dịch, mà thường kết quả trong nó làm cho ý nghĩa hơn với con người).

+0

Xin lỗi, đó là một câu hỏi rác rưởi. Tôi đã cập nhật nó với một ví dụ về những gì tôi đang cố gắng làm. Đóng cửa trên một đóng cửa có thể là một giải pháp, nhưng tôi không chắc chắn về cách bạn sẽ làm điều đó. – Doug

+0

@ Doug, vâng, các câu hỏi không có nguồn thực tế là cực kỳ khó trả lời một cách hữu ích. Đã cập nhật. – huon

0

Đây là mã hoạt động trong 1.22.1

Mục đích là thực hiện công việc này.

let x = |x| { println!("----{}",x)}; 

let mut y = Box::new(x); 

y.clone(); 

Mã gốc như đề xuất ở trên cùng đã được sử dụng.

Tôi bắt đầu với nhân bản một đóng cửa Fn.

type Fp = Box<Fn(i8, i8) -> i8 + Send + Sync>; 

Đã kết thúc thêm Arc xung quanh Fp trong struct WithCall

Play rust : working code Gist : working code in 1.22.1

Các vấn đề liên quan